X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Ftext-controller.cpp;h=13d0ab2a87c436258219f0fbc6c3a9b6300af115;hp=552be5609774d07d909d9a5474a235d360fe566f;hb=60d99d04d7677a1b91d4f2fdd9e74bc5e1b5e7bf;hpb=48d13d45cb7f363d3cf8da048ce6cef074c55060 diff --git a/dali-toolkit/internal/text/text-controller.cpp b/dali-toolkit/internal/text/text-controller.cpp index 552be56..5a8a6e0 100755 --- a/dali-toolkit/internal/text/text-controller.cpp +++ b/dali-toolkit/internal/text/text-controller.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2020 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. @@ -20,11 +20,13 @@ // EXTERNAL INCLUDES #include +#include #include #include #include #include #include +#include // INTERNAL INCLUDES #include @@ -48,6 +50,10 @@ const float MAX_FLOAT = std::numeric_limits::max(); const std::string EMPTY_STRING(""); +const std::string KEY_C_NAME = "c"; +const std::string KEY_V_NAME = "v"; +const std::string KEY_X_NAME = "x"; + const char * const PLACEHOLDER_TEXT = "text"; const char * const PLACEHOLDER_TEXT_FOCUSED = "textFocused"; const char * const PLACEHOLDER_COLOR = "color"; @@ -60,7 +66,17 @@ const char * const PLACEHOLDER_ELLIPSIS = "ellipsis"; float ConvertToEven( float value ) { int intValue(static_cast( value )); - return static_cast(intValue % 2 == 0) ? intValue : (intValue + 1); + return static_cast( intValue + ( intValue & 1 ) ); +} + +int ConvertPixelToPint( float pixel ) +{ + unsigned int horizontalDpi = 0u; + unsigned int verticalDpi = 0u; + Dali::TextAbstraction::FontClient fontClient = Dali::TextAbstraction::FontClient::Get(); + fontClient.GetDpi( horizontalDpi, verticalDpi ); + + return ( pixel * 72.f ) / static_cast< float >( horizontalDpi ); } } // namespace @@ -125,15 +141,17 @@ ControllerPtr Controller::New( ControlInterface* controlInterface ) } ControllerPtr Controller::New( ControlInterface* controlInterface, - EditableControlInterface* editableControlInterface ) + EditableControlInterface* editableControlInterface, + SelectableControlInterface* selectableControlInterface ) { return ControllerPtr( new Controller( controlInterface, - editableControlInterface ) ); + editableControlInterface, + selectableControlInterface ) ); } // public : Configure the text controller. -void Controller::EnableTextInput( DecoratorPtr decorator ) +void Controller::EnableTextInput( DecoratorPtr decorator, InputMethodContext& inputMethodContext ) { if( !decorator ) { @@ -146,7 +164,7 @@ void Controller::EnableTextInput( DecoratorPtr decorator ) if( NULL == mImpl->mEventData ) { - mImpl->mEventData = new EventData( decorator ); + mImpl->mEventData = new EventData( decorator, inputMethodContext ); } } @@ -163,7 +181,14 @@ void Controller::SetGlyphType( TextAbstraction::GlyphType glyphType ) void Controller::SetMarkupProcessorEnabled( bool enable ) { - mImpl->mMarkupProcessorEnabled = enable; + if( enable != mImpl->mMarkupProcessorEnabled ) + { + //If Text was already set, call the SetText again for enabling or disabling markup + mImpl->mMarkupProcessorEnabled = enable; + std::string text; + GetText( text ); + SetText( text ); + } } bool Controller::IsMarkupProcessorEnabled() const @@ -217,7 +242,7 @@ bool Controller::IsAutoScrollEnabled() const CharacterDirection Controller::GetAutoScrollDirection() const { - return mImpl->mAutoScrollDirectionRTL; + return mImpl->mIsTextDirectionRTL; } float Controller::GetAutoScrollLineAlignment() const @@ -346,8 +371,12 @@ void Controller::SetMultiLineEnabled( bool enable ) ALIGN | REORDER ); + mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true; mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | layoutOperations ); + // Need to recalculate natural size + mImpl->mRecalculateNaturalSize = true; + mImpl->RequestRelayout(); } } @@ -367,6 +396,18 @@ void Controller::SetHorizontalAlignment( Text::HorizontalAlignment::Type alignme // Set the flag to redo the alignment operation. mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | ALIGN ); + if( mImpl->mEventData ) + { + mImpl->mEventData->mUpdateAlignment = true; + + // Update the cursor if it's in editing mode + if( EventData::IsEditingState( mImpl->mEventData->mState ) ) + { + mImpl->ChangeState( EventData::EDITING ); + mImpl->mEventData->mUpdateCursorPosition = true; + } + } + mImpl->RequestRelayout(); } } @@ -394,6 +435,37 @@ VerticalAlignment::Type Controller::GetVerticalAlignment() const return mImpl->mModel->mVerticalAlignment; } +bool Controller::IsIgnoreSpacesAfterText() const +{ + return mImpl->mModel->mIgnoreSpacesAfterText; +} + +void Controller::SetIgnoreSpacesAfterText( bool ignore ) +{ + mImpl->mModel->mIgnoreSpacesAfterText = ignore; +} + +bool Controller::IsMatchSystemLanguageDirection() const +{ + return mImpl->mModel->mMatchSystemLanguageDirection; +} + +void Controller::SetMatchSystemLanguageDirection( bool match ) +{ + mImpl->mModel->mMatchSystemLanguageDirection = match; +} + +void Controller::SetLayoutDirection( Dali::LayoutDirection::Type layoutDirection ) +{ + mImpl->mLayoutDirection = layoutDirection; +} + +bool Controller::IsShowingRealText() const +{ + return mImpl->IsShowingRealText(); +} + + void Controller::SetLineWrapMode( Text::LineWrap::Mode lineWrapMode ) { if( lineWrapMode != mImpl->mModel->mLineWrapMode ) @@ -432,6 +504,92 @@ bool Controller::IsTextElideEnabled() const return mImpl->mModel->mElideEnabled; } +void Controller::SetTextFitEnabled(bool enabled) +{ + mImpl->mTextFitEnabled = enabled; +} + +bool Controller::IsTextFitEnabled() const +{ + return mImpl->mTextFitEnabled; +} + +void Controller::SetTextFitMinSize( float minSize, FontSizeType type ) +{ + switch( type ) + { + case POINT_SIZE: + { + mImpl->mTextFitMinSize = minSize; + break; + } + case PIXEL_SIZE: + { + mImpl->mTextFitMinSize = ConvertPixelToPint( minSize ); + break; + } + } +} + +float Controller::GetTextFitMinSize() const +{ + return mImpl->mTextFitMinSize; +} + +void Controller::SetTextFitMaxSize( float maxSize, FontSizeType type ) +{ + switch( type ) + { + case POINT_SIZE: + { + mImpl->mTextFitMaxSize = maxSize; + break; + } + case PIXEL_SIZE: + { + mImpl->mTextFitMaxSize = ConvertPixelToPint( maxSize ); + break; + } + } +} + +float Controller::GetTextFitMaxSize() const +{ + return mImpl->mTextFitMaxSize; +} + +void Controller::SetTextFitStepSize( float step, FontSizeType type ) +{ + switch( type ) + { + case POINT_SIZE: + { + mImpl->mTextFitStepSize = step; + break; + } + case PIXEL_SIZE: + { + mImpl->mTextFitStepSize = ConvertPixelToPint( step ); + break; + } + } +} + +float Controller::GetTextFitStepSize() const +{ + return mImpl->mTextFitStepSize; +} + +void Controller::SetTextFitContentSize(Vector2 size) +{ + mImpl->mTextFitContentSize = size; +} + +Vector2 Controller::GetTextFitContentSize() const +{ + return mImpl->mTextFitContentSize; +} + void Controller::SetPlaceholderTextElideEnabled( bool enabled ) { mImpl->mEventData->mIsPlaceholderElideEnabled = enabled; @@ -460,6 +618,36 @@ bool Controller::IsSelectionEnabled() const return mImpl->mEventData->mSelectionEnabled; } +void Controller::SetShiftSelectionEnabled( bool enabled ) +{ + mImpl->mEventData->mShiftSelectionFlag = enabled; +} + +bool Controller::IsShiftSelectionEnabled() const +{ + return mImpl->mEventData->mShiftSelectionFlag; +} + +void Controller::SetGrabHandleEnabled( bool enabled ) +{ + mImpl->mEventData->mGrabHandleEnabled = enabled; +} + +bool Controller::IsGrabHandleEnabled() const +{ + return mImpl->mEventData->mGrabHandleEnabled; +} + +void Controller::SetGrabHandlePopupEnabled(bool enabled) +{ + mImpl->mEventData->mGrabHandlePopupEnabled = enabled; +} + +bool Controller::IsGrabHandlePopupEnabled() const +{ + return mImpl->mEventData->mGrabHandlePopupEnabled; +} + // public : Update void Controller::SetText( const std::string& text ) @@ -467,7 +655,7 @@ void Controller::SetText( const std::string& text ) DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText\n" ); // Reset keyboard as text changed - mImpl->ResetImfManager(); + mImpl->ResetInputMethodContext(); // Remove the previously set text and style. ResetText(); @@ -494,7 +682,8 @@ void Controller::SetText( const std::string& text ) mImpl->mModel->mVisualModel->SetTextColor( mImpl->mTextColor ); MarkupProcessData markupProcessData( mImpl->mModel->mLogicalModel->mColorRuns, - mImpl->mModel->mLogicalModel->mFontDescriptionRuns ); + mImpl->mModel->mLogicalModel->mFontDescriptionRuns, + mImpl->mModel->mLogicalModel->mEmbeddedItems ); Length textSize = 0u; const uint8_t* utf8 = NULL; @@ -538,6 +727,9 @@ void Controller::SetText( const std::string& text ) // The natural size needs to be re-calculated. mImpl->mRecalculateNaturalSize = true; + // The text direction needs to be updated. + mImpl->mUpdateTextDirection = true; + // Apply modifications to the model mImpl->mOperationsPending = ALL_OPERATIONS; } @@ -645,6 +837,16 @@ void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily ) DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetDefaultFontFamily %s\n", defaultFontFamily.c_str()); mImpl->mFontDefaults->familyDefined = !defaultFontFamily.empty(); + if( mImpl->mEventData ) + { + // Update the cursor position if it's in editing mode + if( EventData::IsEditingState( mImpl->mEventData->mState ) ) + { + mImpl->mEventData->mDecoratorUpdated = true; + mImpl->mEventData->mUpdateCursorPosition = true; // Cursor position should be updated when the font family is updated. + } + } + // Clear the font-specific data ClearFontData(); @@ -698,6 +900,16 @@ void Controller::SetDefaultFontWeight( FontWeight weight ) mImpl->mFontDefaults->mFontDescription.weight = weight; mImpl->mFontDefaults->weightDefined = true; + if( mImpl->mEventData ) + { + // Update the cursor position if it's in editing mode + if( EventData::IsEditingState( mImpl->mEventData->mState ) ) + { + mImpl->mEventData->mDecoratorUpdated = true; + mImpl->mEventData->mUpdateCursorPosition = true; // Cursor position should be updated when the font weight is updated. + } + } + // Clear the font-specific data ClearFontData(); @@ -769,6 +981,16 @@ void Controller::SetDefaultFontWidth( FontWidth width ) mImpl->mFontDefaults->mFontDescription.width = width; mImpl->mFontDefaults->widthDefined = true; + if( mImpl->mEventData ) + { + // Update the cursor position if it's in editing mode + if( EventData::IsEditingState( mImpl->mEventData->mState ) ) + { + mImpl->mEventData->mDecoratorUpdated = true; + mImpl->mEventData->mUpdateCursorPosition = true; // Cursor position should be updated when the font width is updated. + } + } + // Clear the font-specific data ClearFontData(); @@ -840,6 +1062,16 @@ void Controller::SetDefaultFontSlant( FontSlant slant ) mImpl->mFontDefaults->mFontDescription.slant = slant; mImpl->mFontDefaults->slantDefined = true; + if( mImpl->mEventData ) + { + // Update the cursor position if it's in editing mode + if( EventData::IsEditingState( mImpl->mEventData->mState ) ) + { + mImpl->mEventData->mDecoratorUpdated = true; + mImpl->mEventData->mUpdateCursorPosition = true; // Cursor position should be updated when the font slant is updated. + } + } + // Clear the font-specific data ClearFontData(); @@ -929,6 +1161,16 @@ void Controller::SetDefaultFontSize( float fontSize, FontSizeType type ) } } + if( mImpl->mEventData ) + { + // Update the cursor position if it's in editing mode + if( EventData::IsEditingState( mImpl->mEventData->mState ) ) + { + mImpl->mEventData->mDecoratorUpdated = true; + mImpl->mEventData->mUpdateCursorPosition = true; // Cursor position should be updated when the font size is updated. + } + } + // Clear the font-specific data ClearFontData(); @@ -1056,6 +1298,10 @@ void Controller::SetDefaultColor( const Vector4& color ) { mImpl->mModel->mVisualModel->SetTextColor( color ); + mImpl->mModel->mLogicalModel->mColorRuns.Clear(); + + mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | COLOR ); + mImpl->RequestRelayout(); } } @@ -1176,18 +1422,42 @@ const Vector4& Controller::GetOutlineColor() const return mImpl->mModel->mVisualModel->GetOutlineColor(); } -void Controller::SetOutlineWidth( float width ) +void Controller::SetOutlineWidth( uint16_t width ) { mImpl->mModel->mVisualModel->SetOutlineWidth( width ); mImpl->RequestRelayout(); } -float Controller::GetOutlineWidth() const +uint16_t Controller::GetOutlineWidth() const { return mImpl->mModel->mVisualModel->GetOutlineWidth(); } +void Controller::SetBackgroundColor( const Vector4& color ) +{ + mImpl->mModel->mVisualModel->SetBackgroundColor( color ); + + mImpl->RequestRelayout(); +} + +const Vector4& Controller::GetBackgroundColor() const +{ + return mImpl->mModel->mVisualModel->GetBackgroundColor(); +} + +void Controller::SetBackgroundEnabled( bool enabled ) +{ + mImpl->mModel->mVisualModel->SetBackgroundEnabled( enabled ); + + mImpl->RequestRelayout(); +} + +bool Controller::IsBackgroundEnabled() const +{ + return mImpl->mModel->mVisualModel->IsBackgroundEnabled(); +} + void Controller::SetDefaultEmbossProperties( const std::string& embossProperties ) { if( NULL == mImpl->mEmbossDefaults ) @@ -1228,10 +1498,15 @@ const std::string& Controller::GetDefaultOutlineProperties() const return EMPTY_STRING; } -void Controller::SetDefaultLineSpacing( float lineSpacing ) +bool Controller::SetDefaultLineSpacing( float lineSpacing ) { - //TODO finish implementation - mImpl->mLayoutEngine.SetDefaultLineSpacing( lineSpacing ); + if( std::fabs( lineSpacing - mImpl->mLayoutEngine.GetDefaultLineSpacing() ) > Math::MACHINE_EPSILON_1000 ) + { + mImpl->mLayoutEngine.SetDefaultLineSpacing(lineSpacing); + mImpl->mRecalculateNaturalSize = true; + return true; + } + return false; } float Controller::GetDefaultLineSpacing() const @@ -1239,6 +1514,22 @@ float Controller::GetDefaultLineSpacing() const return mImpl->mLayoutEngine.GetDefaultLineSpacing(); } +bool Controller::SetDefaultLineSize( float lineSize ) +{ + if( std::fabs( lineSize - mImpl->mLayoutEngine.GetDefaultLineSize() ) > Math::MACHINE_EPSILON_1000 ) + { + mImpl->mLayoutEngine.SetDefaultLineSize(lineSize); + mImpl->mRecalculateNaturalSize = true; + return true; + } + return false; +} + +float Controller::GetDefaultLineSize() const +{ + return mImpl->mLayoutEngine.GetDefaultLineSize(); +} + void Controller::SetInputColor( const Vector4& color ) { if( NULL != mImpl->mEventData ) @@ -1246,7 +1537,7 @@ void Controller::SetInputColor( const Vector4& color ) mImpl->mEventData->mInputStyle.textColor = color; mImpl->mEventData->mInputStyle.isDefaultColor = false; - if( EventData::SELECTING == mImpl->mEventData->mState ) + if( EventData::SELECTING == mImpl->mEventData->mState || EventData::EDITING == mImpl->mEventData->mState || EventData::INACTIVE == mImpl->mEventData->mState ) { const bool handlesCrossed = mImpl->mEventData->mLeftSelectionPosition > mImpl->mEventData->mRightSelectionPosition; @@ -1293,21 +1584,36 @@ void Controller::SetInputFontFamily( const std::string& fontFamily ) mImpl->mEventData->mInputStyle.familyName = fontFamily; mImpl->mEventData->mInputStyle.isFamilyDefined = true; - if( EventData::SELECTING == mImpl->mEventData->mState ) + if( EventData::SELECTING == mImpl->mEventData->mState || EventData::EDITING == mImpl->mEventData->mState || EventData::INACTIVE == mImpl->mEventData->mState ) { CharacterIndex startOfSelectedText = 0u; Length lengthOfSelectedText = 0u; - FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData, - mImpl->mModel->mLogicalModel, - startOfSelectedText, - lengthOfSelectedText ); - fontDescriptionRun.familyLength = fontFamily.size(); - fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength]; - memcpy( fontDescriptionRun.familyName, fontFamily.c_str(), fontDescriptionRun.familyLength ); - fontDescriptionRun.familyDefined = true; + if( EventData::SELECTING == mImpl->mEventData->mState ) + { + // Update a font description run for the selecting state. + FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData, + mImpl->mModel->mLogicalModel, + startOfSelectedText, + lengthOfSelectedText ); - // The memory allocated for the font family name is freed when the font description is removed from the logical model. + fontDescriptionRun.familyLength = fontFamily.size(); + fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength]; + memcpy( fontDescriptionRun.familyName, fontFamily.c_str(), fontDescriptionRun.familyLength ); + fontDescriptionRun.familyDefined = true; + + // The memory allocated for the font family name is freed when the font description is removed from the logical model. + + mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText; + mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; + mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText; + } + else + { + mImpl->mTextUpdateInfo.mCharacterIndex = 0; + mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters; + mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count(); + } // Request to relayout. mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | @@ -1321,10 +1627,6 @@ void Controller::SetInputFontFamily( const std::string& fontFamily ) mImpl->mRecalculateNaturalSize = true; mImpl->RequestRelayout(); - mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText; - mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; - mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText; - // As the font changes, recalculate the handle positions is needed. mImpl->mEventData->mUpdateLeftSelectionPosition = true; mImpl->mEventData->mUpdateRightSelectionPosition = true; @@ -1352,17 +1654,32 @@ void Controller::SetInputFontWeight( FontWeight weight ) mImpl->mEventData->mInputStyle.weight = weight; mImpl->mEventData->mInputStyle.isWeightDefined = true; - if( EventData::SELECTING == mImpl->mEventData->mState ) + if( EventData::SELECTING == mImpl->mEventData->mState || EventData::EDITING == mImpl->mEventData->mState || EventData::INACTIVE == mImpl->mEventData->mState ) { CharacterIndex startOfSelectedText = 0u; Length lengthOfSelectedText = 0u; - FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData, - mImpl->mModel->mLogicalModel, - startOfSelectedText, - lengthOfSelectedText ); - fontDescriptionRun.weight = weight; - fontDescriptionRun.weightDefined = true; + if( EventData::SELECTING == mImpl->mEventData->mState ) + { + // Update a font description run for the selecting state. + FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData, + mImpl->mModel->mLogicalModel, + startOfSelectedText, + lengthOfSelectedText ); + + fontDescriptionRun.weight = weight; + fontDescriptionRun.weightDefined = true; + + mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText; + mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; + mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText; + } + else + { + mImpl->mTextUpdateInfo.mCharacterIndex = 0; + mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters; + mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count(); + } // Request to relayout. mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | @@ -1376,10 +1693,6 @@ void Controller::SetInputFontWeight( FontWeight weight ) mImpl->mRecalculateNaturalSize = true; mImpl->RequestRelayout(); - mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText; - mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; - mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText; - // As the font might change, recalculate the handle positions is needed. mImpl->mEventData->mUpdateLeftSelectionPosition = true; mImpl->mEventData->mUpdateRightSelectionPosition = true; @@ -1418,17 +1731,32 @@ void Controller::SetInputFontWidth( FontWidth width ) mImpl->mEventData->mInputStyle.width = width; mImpl->mEventData->mInputStyle.isWidthDefined = true; - if( EventData::SELECTING == mImpl->mEventData->mState ) + if( EventData::SELECTING == mImpl->mEventData->mState || EventData::EDITING == mImpl->mEventData->mState || EventData::INACTIVE == mImpl->mEventData->mState ) { CharacterIndex startOfSelectedText = 0u; Length lengthOfSelectedText = 0u; - FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData, - mImpl->mModel->mLogicalModel, - startOfSelectedText, - lengthOfSelectedText ); - fontDescriptionRun.width = width; - fontDescriptionRun.widthDefined = true; + if( EventData::SELECTING == mImpl->mEventData->mState ) + { + // Update a font description run for the selecting state. + FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData, + mImpl->mModel->mLogicalModel, + startOfSelectedText, + lengthOfSelectedText ); + + fontDescriptionRun.width = width; + fontDescriptionRun.widthDefined = true; + + mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText; + mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; + mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText; + } + else + { + mImpl->mTextUpdateInfo.mCharacterIndex = 0; + mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters; + mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count(); + } // Request to relayout. mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | @@ -1442,10 +1770,6 @@ void Controller::SetInputFontWidth( FontWidth width ) mImpl->mRecalculateNaturalSize = true; mImpl->RequestRelayout(); - mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText; - mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; - mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText; - // As the font might change, recalculate the handle positions is needed. mImpl->mEventData->mUpdateLeftSelectionPosition = true; mImpl->mEventData->mUpdateRightSelectionPosition = true; @@ -1484,17 +1808,32 @@ void Controller::SetInputFontSlant( FontSlant slant ) mImpl->mEventData->mInputStyle.slant = slant; mImpl->mEventData->mInputStyle.isSlantDefined = true; - if( EventData::SELECTING == mImpl->mEventData->mState ) + if( EventData::SELECTING == mImpl->mEventData->mState || EventData::EDITING == mImpl->mEventData->mState || EventData::INACTIVE == mImpl->mEventData->mState ) { CharacterIndex startOfSelectedText = 0u; Length lengthOfSelectedText = 0u; - FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData, - mImpl->mModel->mLogicalModel, - startOfSelectedText, - lengthOfSelectedText ); - fontDescriptionRun.slant = slant; - fontDescriptionRun.slantDefined = true; + if( EventData::SELECTING == mImpl->mEventData->mState ) + { + // Update a font description run for the selecting state. + FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData, + mImpl->mModel->mLogicalModel, + startOfSelectedText, + lengthOfSelectedText ); + + fontDescriptionRun.slant = slant; + fontDescriptionRun.slantDefined = true; + + mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText; + mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; + mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText; + } + else + { + mImpl->mTextUpdateInfo.mCharacterIndex = 0; + mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters; + mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count(); + } // Request to relayout. mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | @@ -1508,10 +1847,6 @@ void Controller::SetInputFontSlant( FontSlant slant ) mImpl->mRecalculateNaturalSize = true; mImpl->RequestRelayout(); - mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText; - mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; - mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText; - // As the font might change, recalculate the handle positions is needed. mImpl->mEventData->mUpdateLeftSelectionPosition = true; mImpl->mEventData->mUpdateRightSelectionPosition = true; @@ -1550,17 +1885,32 @@ void Controller::SetInputFontPointSize( float size ) mImpl->mEventData->mInputStyle.size = size; mImpl->mEventData->mInputStyle.isSizeDefined = true; - if( EventData::SELECTING == mImpl->mEventData->mState ) + if( EventData::SELECTING == mImpl->mEventData->mState || EventData::EDITING == mImpl->mEventData->mState || EventData::INACTIVE == mImpl->mEventData->mState ) { CharacterIndex startOfSelectedText = 0u; Length lengthOfSelectedText = 0u; - FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData, - mImpl->mModel->mLogicalModel, - startOfSelectedText, - lengthOfSelectedText ); - fontDescriptionRun.size = static_cast( size * 64.f ); - fontDescriptionRun.sizeDefined = true; + if( EventData::SELECTING == mImpl->mEventData->mState ) + { + // Update a font description run for the selecting state. + FontDescriptionRun& fontDescriptionRun = UpdateSelectionFontStyleRun( mImpl->mEventData, + mImpl->mModel->mLogicalModel, + startOfSelectedText, + lengthOfSelectedText ); + + fontDescriptionRun.size = static_cast( size * 64.f ); + fontDescriptionRun.sizeDefined = true; + + mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText; + mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; + mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText; + } + else + { + mImpl->mTextUpdateInfo.mCharacterIndex = 0; + mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters; + mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count(); + } // Request to relayout. mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | @@ -1574,10 +1924,6 @@ void Controller::SetInputFontPointSize( float size ) mImpl->mRecalculateNaturalSize = true; mImpl->RequestRelayout(); - mImpl->mTextUpdateInfo.mCharacterIndex = startOfSelectedText; - mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; - mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = lengthOfSelectedText; - // As the font might change, recalculate the handle positions is needed. mImpl->mEventData->mUpdateLeftSelectionPosition = true; mImpl->mEventData->mUpdateRightSelectionPosition = true; @@ -1813,7 +2159,6 @@ Vector3 Controller::GetNaturalSize() GET_SCRIPTS | VALIDATE_FONTS | GET_LINE_BREAKS | - GET_WORD_BREAKS | BIDI_INFO | SHAPE_TEXT | GET_GLYPH_METRICS ); @@ -1853,6 +2198,7 @@ Vector3 Controller::GetNaturalSize() // Clear the update info. This info will be set the next time the text is updated. mImpl->mTextUpdateInfo.Clear(); + mImpl->mTextUpdateInfo.mClearAll = true; // Restore the actual control's size. mImpl->mModel->mVisualModel->mControlSize = actualControlSize; @@ -1872,6 +2218,97 @@ Vector3 Controller::GetNaturalSize() return naturalSize; } +bool Controller::CheckForTextFit( float pointSize, Size& layoutSize ) +{ + Size textSize; + mImpl->mFontDefaults->mFitPointSize = pointSize; + mImpl->mFontDefaults->sizeDefined = true; + ClearFontData(); + + // Operations that can be done only once until the text changes. + const OperationsMask onlyOnceOperations = static_cast( CONVERT_TO_UTF32 | + GET_SCRIPTS | + VALIDATE_FONTS | + GET_LINE_BREAKS | + BIDI_INFO | + SHAPE_TEXT| + GET_GLYPH_METRICS ); + + mImpl->mTextUpdateInfo.mParagraphCharacterIndex = 0u; + mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters = mImpl->mModel->mLogicalModel->mText.Count(); + + // Make sure the model is up-to-date before layouting + mImpl->UpdateModel( onlyOnceOperations ); + + DoRelayout( Size( layoutSize.width, MAX_FLOAT ), + static_cast( onlyOnceOperations | LAYOUT), + textSize); + + // Clear the update info. This info will be set the next time the text is updated. + mImpl->mTextUpdateInfo.Clear(); + mImpl->mTextUpdateInfo.mClearAll = true; + + if( textSize.width > layoutSize.width || textSize.height > layoutSize.height ) + { + return false; + } + return true; +} + +void Controller::FitPointSizeforLayout( Size layoutSize ) +{ + const OperationsMask operations = mImpl->mOperationsPending; + if( NO_OPERATION != ( UPDATE_LAYOUT_SIZE & operations ) || mImpl->mTextFitContentSize != layoutSize ) + { + bool actualellipsis = mImpl->mModel->mElideEnabled; + float minPointSize = mImpl->mTextFitMinSize; + float maxPointSize = mImpl->mTextFitMaxSize; + float pointInterval = mImpl->mTextFitStepSize; + + mImpl->mModel->mElideEnabled = false; + Vector pointSizeArray; + + // check zero value + if( pointInterval < 1.f ) + { + mImpl->mTextFitStepSize = pointInterval = 1.0f; + } + + pointSizeArray.Reserve( static_cast< unsigned int >( ceil( ( maxPointSize - minPointSize ) / pointInterval ) ) ); + + for( float i = minPointSize; i < maxPointSize; i += pointInterval ) + { + pointSizeArray.PushBack( i ); + } + + pointSizeArray.PushBack( maxPointSize ); + + int bestSizeIndex = 0; + int min = bestSizeIndex + 1; + int max = pointSizeArray.Size() - 1; + while( min <= max ) + { + int destI = ( min + max ) / 2; + + if( CheckForTextFit( pointSizeArray[destI], layoutSize ) ) + { + bestSizeIndex = min; + min = destI + 1; + } + else + { + max = destI - 1; + bestSizeIndex = max; + } + } + + mImpl->mModel->mElideEnabled = actualellipsis; + mImpl->mFontDefaults->mFitPointSize = pointSizeArray[bestSizeIndex]; + mImpl->mFontDefaults->sizeDefined = true; + ClearFontData(); + } +} + float Controller::GetHeightForWidth( float width ) { DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetHeightForWidth %p width %f\n", this, width ); @@ -1888,7 +2325,6 @@ float Controller::GetHeightForWidth( float width ) GET_SCRIPTS | VALIDATE_FONTS | GET_LINE_BREAKS | - GET_WORD_BREAKS | BIDI_INFO | SHAPE_TEXT | GET_GLYPH_METRICS ); @@ -1924,6 +2360,7 @@ float Controller::GetHeightForWidth( float width ) // Clear the update info. This info will be set the next time the text is updated. mImpl->mTextUpdateInfo.Clear(); + mImpl->mTextUpdateInfo.mClearAll = true; // Restore the actual control's width. mImpl->mModel->mVisualModel->mControlSize.width = actualControlWidth; @@ -2097,9 +2534,63 @@ void Controller::GetPlaceholderProperty( Property::Map& map ) } } +Toolkit::DevelText::TextDirection::Type Controller::GetTextDirection() +{ + // Make sure the model is up-to-date before layouting + ProcessModifyEvents(); + + if ( mImpl->mUpdateTextDirection ) + { + // Operations that can be done only once until the text changes. + const OperationsMask onlyOnceOperations = static_cast( CONVERT_TO_UTF32 | + GET_SCRIPTS | + VALIDATE_FONTS | + GET_LINE_BREAKS | + BIDI_INFO | + SHAPE_TEXT | + GET_GLYPH_METRICS ); + + // Set the update info to relayout the whole text. + mImpl->mTextUpdateInfo.mParagraphCharacterIndex = 0u; + mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters = mImpl->mModel->mLogicalModel->mText.Count(); + + // Make sure the model is up-to-date before layouting + mImpl->UpdateModel( onlyOnceOperations ); + + Vector3 naturalSize; + DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ), + static_cast( onlyOnceOperations | + LAYOUT | REORDER | UPDATE_DIRECTION ), + naturalSize.GetVectorXY() ); + + // Do not do again the only once operations. + mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending & ~onlyOnceOperations ); + + // Clear the update info. This info will be set the next time the text is updated. + mImpl->mTextUpdateInfo.Clear(); + + // FullRelayoutNeeded should be true because DoRelayout is MAX_FLOAT, MAX_FLOAT. + mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true; + + mImpl->mUpdateTextDirection = false; + } + + return mImpl->mIsTextDirectionRTL ? Toolkit::DevelText::TextDirection::RIGHT_TO_LEFT : Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT; +} + +Toolkit::DevelText::VerticalLineAlignment::Type Controller::GetVerticalLineAlignment() const +{ + return mImpl->mModel->GetVerticalLineAlignment(); +} + +void Controller::SetVerticalLineAlignment( Toolkit::DevelText::VerticalLineAlignment::Type alignment ) +{ + mImpl->mModel->mVerticalLineAlignment = alignment; +} + // public : Relayout. -Controller::UpdateTextType Controller::Relayout( const Size& size ) +Controller::UpdateTextType Controller::Relayout( const Size& size, Dali::LayoutDirection::Type layoutDirection ) { DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f, autoScroll[%s]\n", this, size.width, size.height, mImpl->mIsAutoScrollEnabled ?"true":"false" ); @@ -2129,6 +2620,13 @@ Controller::UpdateTextType Controller::Relayout( const Size& size ) { DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mModel->mVisualModel->mControlSize.width, mImpl->mModel->mVisualModel->mControlSize.height ); + if( ( 0 == mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd ) && + ( 0 == mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters ) && + ( ( mImpl->mModel->mVisualModel->mControlSize.width < Math::MACHINE_EPSILON_1000 ) || ( mImpl->mModel->mVisualModel->mControlSize.height < Math::MACHINE_EPSILON_1000 ) ) ) + { + mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count(); + } + // Layout operations that need to be done if the size changes. mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | LAYOUT | @@ -2165,6 +2663,22 @@ Controller::UpdateTextType Controller::Relayout( const Size& size ) mImpl->mTextUpdateInfo.mCharacterIndex = 0u; } + if( mImpl->mModel->mMatchSystemLanguageDirection && mImpl->mLayoutDirection != layoutDirection ) + { + // Clear the update info. This info will be set the next time the text is updated. + mImpl->mTextUpdateInfo.mClearAll = true; + // Apply modifications to the model + // Shape the text again is needed because characters like '()[]{}' have to be mirrored and the glyphs generated again. + mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | + GET_GLYPH_METRICS | + SHAPE_TEXT | + UPDATE_DIRECTION | + LAYOUT | + BIDI_INFO | + REORDER ); + mImpl->mLayoutDirection = layoutDirection; + } + // Make sure the model is up-to-date before layouting. ProcessModifyEvents(); bool updated = mImpl->UpdateModel( mImpl->mOperationsPending ); @@ -2175,6 +2689,7 @@ Controller::UpdateTextType Controller::Relayout( const Size& size ) mImpl->mOperationsPending, layoutSize ) || updated; + if( updated ) { updateTextType = MODEL_UPDATED; @@ -2276,8 +2791,9 @@ void Controller::KeyboardFocusGainEvent() mImpl->ChangeState( EventData::EDITING ); mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered. mImpl->mEventData->mUpdateInputStyle = true; + mImpl->mEventData->mScrollAfterUpdatePosition = true; } - mImpl->NotifyImfMultiLineStatus(); + mImpl->NotifyInputMethodContextMultiLineStatus(); if( mImpl->IsShowingPlaceholderText() ) { // Show alternative placeholder-text when editing @@ -2316,10 +2832,11 @@ bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent ) bool relayoutNeeded = false; if( ( NULL != mImpl->mEventData ) && - ( keyEvent.state == KeyEvent::Down ) ) + ( keyEvent.GetState() == KeyEvent::DOWN ) ) { - int keyCode = keyEvent.keyCode; - const std::string& keyString = keyEvent.keyPressed; + int keyCode = keyEvent.GetKeyCode(); + const std::string& keyString = keyEvent.GetKeyString(); + const std::string keyName = keyEvent.GetKeyName(); const bool isNullKey = ( 0 == keyCode ) && ( keyString.empty() ); @@ -2352,27 +2869,71 @@ bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent ) uint32_t numberOfLines = mImpl->mModel->GetNumberOfLines(); // Logic to determine whether this text control will lose focus or not. - if( ( Dali::DALI_KEY_CURSOR_LEFT == keyCode && 0 == cursorPosition ) || - ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode && numberOfCharacters == cursorPosition) || + if( ( Dali::DALI_KEY_CURSOR_LEFT == keyCode && 0 == cursorPosition && !keyEvent.IsShiftModifier() ) || + ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode && numberOfCharacters == cursorPosition && !keyEvent.IsShiftModifier() ) || ( Dali::DALI_KEY_CURSOR_DOWN == keyCode && cursorLine == numberOfLines -1 ) || ( Dali::DALI_KEY_CURSOR_DOWN == keyCode && numberOfCharacters == cursorPosition && cursorLine -1 == numberOfLines -1 ) || ( Dali::DALI_KEY_CURSOR_UP == keyCode && cursorLine == 0 ) || ( Dali::DALI_KEY_CURSOR_UP == keyCode && numberOfCharacters == cursorPosition && cursorLine == 1 ) ) { + // Release the active highlight. + if( mImpl->mEventData->mState == EventData::SELECTING ) + { + mImpl->ChangeState( EventData::EDITING ); + + // Update selection position. + mImpl->mEventData->mLeftSelectionPosition = mImpl->mEventData->mPrimaryCursorPosition; + mImpl->mEventData->mRightSelectionPosition = mImpl->mEventData->mPrimaryCursorPosition; + mImpl->mEventData->mUpdateCursorPosition = true; + mImpl->RequestRelayout(); + } return false; } mImpl->mEventData->mCheckScrollAmount = true; Event event( Event::CURSOR_KEY_EVENT ); event.p1.mInt = keyCode; + event.p2.mBool = keyEvent.IsShiftModifier(); mImpl->mEventData->mEventQueue.push_back( event ); // Will request for relayout. relayoutNeeded = true; } - else if( Dali::DALI_KEY_BACKSPACE == keyCode ) + else if ( Dali::DevelKey::DALI_KEY_CONTROL_LEFT == keyCode || Dali::DevelKey::DALI_KEY_CONTROL_RIGHT == keyCode ) { - textChanged = BackspaceKeyEvent(); + // Left or Right Control key event is received before Ctrl-C/V/X key event is received + // If not handle it here, any selected text will be deleted + + // Do nothing + return false; + } + else if ( keyEvent.IsCtrlModifier() ) + { + bool consumed = false; + if (keyName == KEY_C_NAME) + { + // Ctrl-C to copy the selected text + TextPopupButtonTouched( Toolkit::TextSelectionPopup::COPY ); + consumed = true; + } + else if (keyName == KEY_V_NAME) + { + // Ctrl-V to paste the copied text + TextPopupButtonTouched( Toolkit::TextSelectionPopup::PASTE ); + consumed = true; + } + else if (keyName == KEY_X_NAME) + { + // Ctrl-X to cut the selected text + TextPopupButtonTouched( Toolkit::TextSelectionPopup::CUT ); + consumed = true; + } + return consumed; + } + else if( ( Dali::DALI_KEY_BACKSPACE == keyCode ) || + ( Dali::DevelKey::DALI_KEY_DELETE == keyCode ) ) + { + textChanged = DeleteEvent( keyCode ); // Will request for relayout. relayoutNeeded = true; @@ -2389,9 +2950,9 @@ bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent ) // This branch avoids calling the InsertText() method of the 'else' branch which can delete selected text. } - else if( Dali::DALI_KEY_SHIFT_LEFT == keyCode ) + else if( ( Dali::DALI_KEY_SHIFT_LEFT == keyCode ) || ( Dali::DALI_KEY_SHIFT_RIGHT == keyCode ) ) { - // DALI_KEY_SHIFT_LEFT is the key code for the Left Shift. It's sent (by the imf?) when the predictive text is enabled + // DALI_KEY_SHIFT_LEFT or DALI_KEY_SHIFT_RIGHT is the key code for Shift. It's sent (by the InputMethodContext?) when the predictive text is enabled // and a character is typed after the type of a upper case latin character. // Do nothing. @@ -2407,26 +2968,32 @@ bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent ) { DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() ); - // IMF manager is no longer handling key-events - mImpl->ClearPreEditFlag(); + if( !keyString.empty() ) + { + // InputMethodContext is no longer handling key-events + mImpl->ClearPreEditFlag(); - InsertText( keyString, COMMIT ); - textChanged = true; + InsertText( keyString, COMMIT ); + + textChanged = true; + + // Will request for relayout. + relayoutNeeded = true; + } - // Will request for relayout. - relayoutNeeded = true; } if ( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) && ( mImpl->mEventData->mState != EventData::INACTIVE ) && ( !isNullKey ) && ( Dali::DALI_KEY_SHIFT_LEFT != keyCode ) && + ( Dali::DALI_KEY_SHIFT_RIGHT != keyCode ) && ( Dali::DALI_KEY_VOLUME_UP != keyCode ) && ( Dali::DALI_KEY_VOLUME_DOWN != keyCode ) ) { - // Should not change the state if the key is the shift send by the imf manager. + // Should not change the state if the key is the shift send by the InputMethodContext. // Otherwise, when the state is SELECTING the text controller can't send the right - // surrounding info to the imf. + // surrounding info to the InputMethodContext. mImpl->ChangeState( EventData::EDITING ); // Will request for relayout. @@ -2523,17 +3090,17 @@ void Controller::TapEvent( unsigned int tapCount, float x, float y ) } // Reset keyboard as tap event has occurred. - mImpl->ResetImfManager(); + mImpl->ResetInputMethodContext(); } -void Controller::PanEvent( Gesture::State state, const Vector2& displacement ) +void Controller::PanEvent( GestureState state, const Vector2& displacement ) { DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" ); if( NULL != mImpl->mEventData ) { Event event( Event::PAN_EVENT ); - event.p1.mInt = state; + event.p1.mInt = static_cast( state ); event.p2.mFloat = displacement.x; event.p3.mFloat = displacement.y; mImpl->mEventData->mEventQueue.push_back( event ); @@ -2542,11 +3109,11 @@ void Controller::PanEvent( Gesture::State state, const Vector2& displacement ) } } -void Controller::LongPressEvent( Gesture::State state, float x, float y ) +void Controller::LongPressEvent( GestureState state, float x, float y ) { DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected LongPressEvent" ); - if( ( state == Gesture::Started ) && + if( ( state == GestureState::STARTED ) && ( NULL != mImpl->mEventData ) ) { // The 1st long-press on inactive text-field is treated as tap @@ -2565,7 +3132,7 @@ void Controller::LongPressEvent( Gesture::State state, float x, float y ) else if( !mImpl->IsShowingRealText() ) { Event event( Event::LONG_PRESS_EVENT ); - event.p1.mInt = state; + event.p1.mInt = static_cast( state ); event.p2.mFloat = x; event.p3.mFloat = y; mImpl->mEventData->mEventQueue.push_back( event ); @@ -2573,11 +3140,11 @@ void Controller::LongPressEvent( Gesture::State state, float x, float y ) } else if( !mImpl->IsClipboardVisible() ) { - // Reset the imf manager to commit the pre-edit before selecting the text. - mImpl->ResetImfManager(); + // Reset the InputMethodContext to commit the pre-edit before selecting the text. + mImpl->ResetInputMethodContext(); Event event( Event::LONG_PRESS_EVENT ); - event.p1.mInt = state; + event.p1.mInt = static_cast( state ); event.p2.mFloat = x; event.p3.mFloat = y; mImpl->mEventData->mEventQueue.push_back( event ); @@ -2589,35 +3156,84 @@ void Controller::LongPressEvent( Gesture::State state, float x, float y ) } } -ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent ) +void Controller::SelectEvent( float x, float y, SelectionType selectType ) +{ + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SelectEvent\n" ); + + if( NULL != mImpl->mEventData ) + { + if( selectType == SelectionType::ALL ) + { + Event event( Event::SELECT_ALL ); + mImpl->mEventData->mEventQueue.push_back( event ); + } + else if( selectType == SelectionType::NONE ) + { + Event event( Event::SELECT_NONE ); + mImpl->mEventData->mEventQueue.push_back( event ); + } + else + { + Event event( Event::SELECT ); + event.p2.mFloat = x; + event.p3.mFloat = y; + mImpl->mEventData->mEventQueue.push_back( event ); + } + + mImpl->mEventData->mCheckScrollAmount = true; + mImpl->mEventData->mIsLeftHandleSelected = true; + mImpl->mEventData->mIsRightHandleSelected = true; + mImpl->RequestRelayout(); + } +} + +void Controller::SetTextSelectionRange(const uint32_t *start, const uint32_t *end) +{ + if( mImpl->mEventData ) + { + mImpl->mEventData->mCheckScrollAmount = true; + mImpl->mEventData->mIsLeftHandleSelected = true; + mImpl->mEventData->mIsRightHandleSelected = true; + mImpl->SetTextSelectionRange(start, end); + mImpl->RequestRelayout(); + KeyboardFocusGainEvent(); + } +} + +Uint32Pair Controller::GetTextSelectionRange() const +{ + return mImpl->GetTextSelectionRange(); +} + +InputMethodContext::CallbackData Controller::OnInputMethodContextEvent( InputMethodContext& inputMethodContext, const InputMethodContext::EventData& inputMethodContextEvent ) { // Whether the text needs to be relaid-out. bool requestRelayout = false; - // Whether to retrieve the text and cursor position to be sent to the IMF manager. + // Whether to retrieve the text and cursor position to be sent to the InputMethodContext. bool retrieveText = false; bool retrieveCursor = false; - switch( imfEvent.eventName ) + switch( inputMethodContextEvent.eventName ) { - case ImfManager::COMMIT: + case InputMethodContext::COMMIT: { - InsertText( imfEvent.predictiveString, Text::Controller::COMMIT ); + InsertText( inputMethodContextEvent.predictiveString, Text::Controller::COMMIT ); requestRelayout = true; retrieveCursor = true; break; } - case ImfManager::PREEDIT: + case InputMethodContext::PRE_EDIT: { - InsertText( imfEvent.predictiveString, Text::Controller::PRE_EDIT ); + InsertText( inputMethodContextEvent.predictiveString, Text::Controller::PRE_EDIT ); requestRelayout = true; retrieveCursor = true; break; } - case ImfManager::DELETESURROUNDING: + case InputMethodContext::DELETE_SURROUNDING: { - const bool textDeleted = RemoveText( imfEvent.cursorOffset, - imfEvent.numberOfChars, + const bool textDeleted = RemoveText( inputMethodContextEvent.cursorOffset, + inputMethodContextEvent.numberOfChars, DONT_UPDATE_INPUT_STYLE ); if( textDeleted ) @@ -2638,20 +3254,20 @@ ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, cons } break; } - case ImfManager::GETSURROUNDING: + case InputMethodContext::GET_SURROUNDING: { retrieveText = true; retrieveCursor = true; break; } - case ImfManager::PRIVATECOMMAND: + case InputMethodContext::PRIVATE_COMMAND: { // PRIVATECOMMAND event is just for getting the private command message retrieveText = true; retrieveCursor = true; break; } - case ImfManager::VOID: + case InputMethodContext::VOID: { // do nothing break; @@ -2686,10 +3302,20 @@ ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, cons if( retrieveText ) { - mImpl->GetText( numberOfWhiteSpaces, text ); + if( !mImpl->IsShowingPlaceholderText() ) + { + // Retrieves the normal text string. + mImpl->GetText( numberOfWhiteSpaces, text ); + } + else + { + // When the current text is Placeholder Text, the surrounding text should be empty string. + // It means DALi should send empty string ("") to IME. + text = ""; + } } - ImfManager::ImfCallbackData callbackData( ( retrieveText || retrieveCursor ), cursorPosition, text, false ); + InputMethodContext::CallbackData callbackData( ( retrieveText || retrieveCursor ), cursorPosition, text, false ); if( requestRelayout && ( NULL != mImpl->mEditableControlInterface ) ) @@ -2708,7 +3334,7 @@ void Controller::PasteClipboardItemEvent() std::string stringToPaste( notifier.GetContent() ); // Commit the current pre-edit text; the contents of the clipboard should be appended - mImpl->ResetImfManager(); + mImpl->ResetInputMethodContext(); // Temporary disable hiding clipboard mImpl->SetClipboardHideEnable( false ); @@ -2846,14 +3472,14 @@ void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Butt if( mImpl->mEventData->mSelectionEnabled ) { // Creates a SELECT event. - SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false ); + SelectEvent( currentCursorPosition.x, currentCursorPosition.y, SelectionType::INTERACTIVE ); } break; } case Toolkit::TextSelectionPopup::SELECT_ALL: { // Creates a SELECT_ALL event - SelectEvent( 0.f, 0.f, true ); + SelectEvent( 0.f, 0.f, SelectionType::ALL ); break; } case Toolkit::TextSelectionPopup::CLIPBOARD: @@ -2900,7 +3526,7 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ // TODO: At the moment the underline runs are only for pre-edit. mImpl->mModel->mVisualModel->mUnderlineRuns.Clear(); - // Remove the previous IMF pre-edit. + // Remove the previous InputMethodContext pre-edit. if( mImpl->mEventData->mPreEditFlag && ( 0u != mImpl->mEventData->mPreEditLength ) ) { removedPrevious = RemoveText( -static_cast( mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition ), @@ -2947,10 +3573,10 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ mImpl->ChangeState( EventData::EDITING ); - // Handle the IMF (predicitive text) state changes + // Handle the InputMethodContext (predicitive text) state changes if( COMMIT == type ) { - // IMF manager is no longer handling key-events + // InputMethodContext is no longer handling key-events mImpl->ClearPreEditFlag(); } else // PRE_EDIT @@ -2992,14 +3618,14 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ mImpl->mModel->mLogicalModel->RetrieveStyle( styleIndex, style ); // Whether to add a new text color run. - const bool addColorRun = ( style.textColor != mImpl->mEventData->mInputStyle.textColor ); + const bool addColorRun = ( style.textColor != mImpl->mEventData->mInputStyle.textColor ) && !mImpl->mEventData->mInputStyle.isDefaultColor; // Whether to add a new font run. - const bool addFontNameRun = style.familyName != mImpl->mEventData->mInputStyle.familyName; - const bool addFontWeightRun = style.weight != mImpl->mEventData->mInputStyle.weight; - const bool addFontWidthRun = style.width != mImpl->mEventData->mInputStyle.width; - const bool addFontSlantRun = style.slant != mImpl->mEventData->mInputStyle.slant; - const bool addFontSizeRun = style.size != mImpl->mEventData->mInputStyle.size; + const bool addFontNameRun = ( style.familyName != mImpl->mEventData->mInputStyle.familyName ) && mImpl->mEventData->mInputStyle.isFamilyDefined; + const bool addFontWeightRun = ( style.weight != mImpl->mEventData->mInputStyle.weight ) && mImpl->mEventData->mInputStyle.isWeightDefined; + const bool addFontWidthRun = ( style.width != mImpl->mEventData->mInputStyle.width ) && mImpl->mEventData->mInputStyle.isWidthDefined; + const bool addFontSlantRun = ( style.slant != mImpl->mEventData->mInputStyle.slant ) && mImpl->mEventData->mInputStyle.isSlantDefined; + const bool addFontSizeRun = ( style.size != mImpl->mEventData->mInputStyle.size ) && mImpl->mEventData->mInputStyle.isSizeDefined ; // Add style runs. if( addColorRun ) @@ -3124,7 +3750,7 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ { DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mModel->mLogicalModel->mText.Count() ); - mImpl->ResetImfManager(); + mImpl->ResetInputMethodContext(); if( NULL != mImpl->mEditableControlInterface ) { @@ -3222,6 +3848,14 @@ bool Controller::RemoveText( int cursorOffset, } } + // If the number of current text and the number of characters to be deleted are same, + // it means all texts should be removed and all Preedit variables should be initialized. + if( ( currentText.Count() - numberOfCharacters == 0 ) && ( cursorIndex == 0 ) ) + { + mImpl->ClearPreEditFlag(); + mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = 0; + } + // Updates the text style runs by removing characters. Runs with no characters are removed. mImpl->mModel->mLogicalModel->UpdateTextStyleRuns( cursorIndex, -numberOfCharacters ); @@ -3236,6 +3870,11 @@ bool Controller::RemoveText( int cursorOffset, mImpl->mEventData->mScrollAfterDelete = true; + if( EventData::INACTIVE == mImpl->mEventData->mState ) + { + mImpl->ChangeState( EventData::EDITING ); + } + DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfCharacters ); removed = true; } @@ -3263,6 +3902,16 @@ bool Controller::RemoveSelectedText() return textRemoved; } +std::string Controller::GetSelectedText() +{ + std::string text; + if( EventData::SELECTING == mImpl->mEventData->mState ) + { + mImpl->RetrieveSelection( text, false ); + } + return text; +} + // private : Relayout. bool Controller::DoRelayout( const Size& size, @@ -3297,6 +3946,22 @@ bool Controller::DoRelayout( const Size& size, const CharacterIndex lastIndex = startIndex + ( ( requestedNumberOfCharacters > 0u ) ? requestedNumberOfCharacters - 1u : 0u ); const GlyphIndex startGlyphIndex = mImpl->mTextUpdateInfo.mStartGlyphIndex; + + // Make sure the index is not out of bound + if ( charactersToGlyph.Count() != glyphsPerCharacter.Count() || + requestedNumberOfCharacters > charactersToGlyph.Count() || + ( lastIndex > charactersToGlyph.Count() && charactersToGlyph.Count() > 0u ) ) + { + std::string currentText; + GetText( currentText ); + + DALI_LOG_ERROR( "Controller::DoRelayout: Attempting to access invalid buffer\n" ); + DALI_LOG_ERROR( "Current text is: %s\n", currentText.c_str() ); + DALI_LOG_ERROR( "startIndex: %u, lastIndex: %u, requestedNumberOfCharacters: %u, charactersToGlyph.Count = %lu, glyphsPerCharacter.Count = %lu\n", startIndex, lastIndex, requestedNumberOfCharacters, charactersToGlyph.Count(), glyphsPerCharacter.Count()); + + return false; + } + const Length numberOfGlyphs = ( requestedNumberOfCharacters > 0u ) ? *( charactersToGlyphBuffer + lastIndex ) + *( glyphsPerCharacterBuffer + lastIndex ) - startGlyphIndex : 0u; const Length totalNumberOfGlyphs = mImpl->mModel->mVisualModel->mGlyphs.Count(); @@ -3312,34 +3977,16 @@ bool Controller::DoRelayout( const Size& size, return true; } - const Vector& lineBreakInfo = mImpl->mModel->mLogicalModel->mLineBreakInfo; - const Vector& wordBreakInfo = mImpl->mModel->mLogicalModel->mWordBreakInfo; - const Vector& characterDirection = mImpl->mModel->mLogicalModel->mCharacterDirections; - const Vector& glyphs = mImpl->mModel->mVisualModel->mGlyphs; - const Vector& glyphsToCharactersMap = mImpl->mModel->mVisualModel->mGlyphsToCharacters; - const Vector& charactersPerGlyph = mImpl->mModel->mVisualModel->mCharactersPerGlyph; - const Character* const textBuffer = mImpl->mModel->mLogicalModel->mText.Begin(); - // Set the layout parameters. Layout::Parameters layoutParameters( size, - textBuffer, - lineBreakInfo.Begin(), - wordBreakInfo.Begin(), - ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL, - glyphs.Begin(), - glyphsToCharactersMap.Begin(), - charactersPerGlyph.Begin(), - charactersToGlyphBuffer, - glyphsPerCharacterBuffer, - totalNumberOfGlyphs, - mImpl->mModel->mHorizontalAlignment, - mImpl->mModel->mLineWrapMode ); + mImpl->mModel); // Resize the vector of positions to have the same size than the vector of glyphs. Vector& glyphPositions = mImpl->mModel->mVisualModel->mGlyphPositions; glyphPositions.Resize( totalNumberOfGlyphs ); // Whether the last character is a new paragraph character. + const Character* const textBuffer = mImpl->mModel->mLogicalModel->mText.Begin(); mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph = TextAbstraction::IsNewParagraph( *( textBuffer + ( mImpl->mModel->mLogicalModel->mText.Count() - 1u ) ) ); layoutParameters.isLastNewParagraph = mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph; @@ -3372,12 +4019,13 @@ bool Controller::DoRelayout( const Size& size, } // Update the visual model. + bool isAutoScrollEnabled = mImpl->mIsAutoScrollEnabled; Size newLayoutSize; viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters, - glyphPositions, - mImpl->mModel->mVisualModel->mLines, newLayoutSize, - elideTextEnabled ); + elideTextEnabled, + isAutoScrollEnabled ); + mImpl->mIsAutoScrollEnabled = isAutoScrollEnabled; viewUpdated = viewUpdated || ( newLayoutSize != layoutSize ); @@ -3387,49 +4035,13 @@ bool Controller::DoRelayout( const Size& size, if( NO_OPERATION != ( UPDATE_DIRECTION & operations ) ) { - mImpl->mAutoScrollDirectionRTL = false; + mImpl->mIsTextDirectionRTL = false; } - // Reorder the lines - if( NO_OPERATION != ( REORDER & operations ) ) + if ( ( NO_OPERATION != ( UPDATE_DIRECTION & operations ) ) && !mImpl->mModel->mVisualModel->mLines.Empty() ) { - Vector& bidirectionalInfo = mImpl->mModel->mLogicalModel->mBidirectionalParagraphInfo; - Vector& bidirectionalLineInfo = mImpl->mModel->mLogicalModel->mBidirectionalLineInfo; - - // Check first if there are paragraphs with bidirectional info. - if( 0u != bidirectionalInfo.Count() ) - { - // Get the lines - const Length numberOfLines = mImpl->mModel->mVisualModel->mLines.Count(); - - // Reorder the lines. - bidirectionalLineInfo.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters. - ReorderLines( bidirectionalInfo, - startIndex, - requestedNumberOfCharacters, - mImpl->mModel->mVisualModel->mLines, - bidirectionalLineInfo ); - - // Set the bidirectional info per line into the layout parameters. - layoutParameters.lineBidirectionalInfoRunsBuffer = bidirectionalLineInfo.Begin(); - layoutParameters.numberOfBidirectionalInfoRuns = bidirectionalLineInfo.Count(); - - // Re-layout the text. Reorder those lines with right to left characters. - mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters, - startIndex, - requestedNumberOfCharacters, - glyphPositions ); - - if ( ( NO_OPERATION != ( UPDATE_DIRECTION & operations ) ) && ( numberOfLines > 0 ) ) - { - const LineRun* const firstline = mImpl->mModel->mVisualModel->mLines.Begin(); - if ( firstline ) - { - mImpl->mAutoScrollDirectionRTL = firstline->direction; - } - } - } - } // REORDER + mImpl->mIsTextDirectionRTL = mImpl->mModel->mVisualModel->mLines[0u].direction; + } // Sets the layout size. if( NO_OPERATION != ( UPDATE_LAYOUT_SIZE & operations ) ) @@ -3444,21 +4056,35 @@ bool Controller::DoRelayout( const Size& size, // The laid-out lines. Vector& lines = mImpl->mModel->mVisualModel->mLines; + CharacterIndex alignStartIndex = startIndex; + Length alignRequestedNumberOfCharacters = requestedNumberOfCharacters; + + // the whole text needs to be full aligned. + // If you do not do a full aligned, only the last line of the multiline input is aligned. + if( mImpl->mEventData && mImpl->mEventData->mUpdateAlignment ) + { + alignStartIndex = 0u; + alignRequestedNumberOfCharacters = mImpl->mModel->mLogicalModel->mText.Count(); + mImpl->mEventData->mUpdateAlignment = false; + } + // Need to align with the control's size as the text may contain lines // starting either with left to right text or right to left. mImpl->mLayoutEngine.Align( size, - startIndex, - requestedNumberOfCharacters, + alignStartIndex, + alignRequestedNumberOfCharacters, mImpl->mModel->mHorizontalAlignment, lines, - mImpl->mModel->mAlignmentOffset ); + mImpl->mModel->mAlignmentOffset, + mImpl->mLayoutDirection, + mImpl->mModel->mMatchSystemLanguageDirection ); viewUpdated = true; } #if defined(DEBUG_ENABLED) std::string currentText; GetText( currentText ); - DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::DoRelayout [%p] mImpl->mAutoScrollDirectionRTL[%s] [%s]\n", this, (mImpl->mAutoScrollDirectionRTL)?"true":"false", currentText.c_str() ); + DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::DoRelayout [%p] mImpl->mIsTextDirectionRTL[%s] [%s]\n", this, (mImpl->mIsTextDirectionRTL)?"true":"false", currentText.c_str() ); #endif DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) ); return viewUpdated; @@ -3538,9 +4164,13 @@ void Controller::ProcessModifyEvents() { // When the text is being modified, delay cursor blinking mImpl->mEventData->mDecorator->DelayCursorBlink(); + + // Update selection position after modifying the text + mImpl->mEventData->mLeftSelectionPosition = mImpl->mEventData->mPrimaryCursorPosition; + mImpl->mEventData->mRightSelectionPosition = mImpl->mEventData->mPrimaryCursorPosition; } - // Discard temporary text + // DISCARD temporary text events.Clear(); } @@ -3549,6 +4179,9 @@ void Controller::TextReplacedEvent() // The natural size needs to be re-calculated. mImpl->mRecalculateNaturalSize = true; + // The text direction needs to be updated. + mImpl->mUpdateTextDirection = true; + // Apply modifications to the model mImpl->mOperationsPending = ALL_OPERATIONS; } @@ -3567,6 +4200,9 @@ void Controller::TextInsertedEvent() // The natural size needs to be re-calculated. mImpl->mRecalculateNaturalSize = true; + // The text direction needs to be updated. + mImpl->mUpdateTextDirection = true; + // Apply modifications to the model; TODO - Optimize this mImpl->mOperationsPending = ALL_OPERATIONS; } @@ -3585,39 +4221,16 @@ void Controller::TextDeletedEvent() // The natural size needs to be re-calculated. mImpl->mRecalculateNaturalSize = true; + // The text direction needs to be updated. + mImpl->mUpdateTextDirection = true; + // Apply modifications to the model; TODO - Optimize this mImpl->mOperationsPending = ALL_OPERATIONS; } -void Controller::SelectEvent( float x, float y, bool selectAll ) -{ - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SelectEvent\n" ); - - if( NULL != mImpl->mEventData ) - { - if( selectAll ) - { - Event event( Event::SELECT_ALL ); - mImpl->mEventData->mEventQueue.push_back( event ); - } - else - { - Event event( Event::SELECT ); - event.p2.mFloat = x; - event.p3.mFloat = y; - mImpl->mEventData->mEventQueue.push_back( event ); - } - - mImpl->mEventData->mCheckScrollAmount = true; - mImpl->mEventData->mIsLeftHandleSelected = true; - mImpl->mEventData->mIsRightHandleSelected = true; - mImpl->RequestRelayout(); - } -} - -bool Controller::BackspaceKeyEvent() +bool Controller::DeleteEvent( int keyCode ) { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this ); + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p KeyCode : %d \n", this, keyCode ); bool removed = false; @@ -3626,20 +4239,27 @@ bool Controller::BackspaceKeyEvent() return removed; } - // IMF manager is no longer handling key-events + // InputMethodContext is no longer handling key-events mImpl->ClearPreEditFlag(); if( EventData::SELECTING == mImpl->mEventData->mState ) { removed = RemoveSelectedText(); } - else if( mImpl->mEventData->mPrimaryCursorPosition > 0 ) + else if( ( mImpl->mEventData->mPrimaryCursorPosition > 0 ) && ( keyCode == Dali::DALI_KEY_BACKSPACE) ) { // Remove the character before the current cursor position removed = RemoveText( -1, 1, UPDATE_INPUT_STYLE ); } + else if( keyCode == Dali::DevelKey::DALI_KEY_DELETE ) + { + // Remove the character after the current cursor position + removed = RemoveText( 0, + 1, + UPDATE_INPUT_STYLE ); + } if( removed ) { @@ -3666,6 +4286,9 @@ void Controller::ResetText() // Reset buffers. mImpl->mModel->mLogicalModel->mText.Clear(); + // Reset the embedded images buffer. + mImpl->mModel->mLogicalModel->ClearEmbeddedImages(); + // We have cleared everything including the placeholder-text mImpl->PlaceholderCleared(); @@ -3679,6 +4302,9 @@ void Controller::ResetText() // The natural size needs to be re-calculated. mImpl->mRecalculateNaturalSize = true; + // The text direction needs to be updated. + mImpl->mUpdateTextDirection = true; + // Apply modifications to the model mImpl->mOperationsPending = ALL_OPERATIONS; } @@ -3745,6 +4371,9 @@ void Controller::ShowPlaceholderText() // The natural size needs to be re-calculated. mImpl->mRecalculateNaturalSize = true; + // The text direction needs to be updated. + mImpl->mUpdateTextDirection = true; + // Apply modifications to the model mImpl->mOperationsPending = ALL_OPERATIONS; @@ -3772,6 +4401,7 @@ void Controller::ClearFontData() mImpl->mOperationsPending = static_cast( mImpl->mOperationsPending | VALIDATE_FONTS | SHAPE_TEXT | + BIDI_INFO | GET_GLYPH_METRICS | LAYOUT | UPDATE_LAYOUT_SIZE | @@ -3820,24 +4450,31 @@ bool Controller::ShouldClearFocusOnEscape() const return mImpl->mShouldClearFocusOnEscape; } +Actor Controller::CreateBackgroundActor() +{ + return mImpl->CreateBackgroundActor(); +} + // private : Private contructors & copy operator. Controller::Controller() : mImpl( NULL ) { - mImpl = new Controller::Impl( NULL, NULL ); + mImpl = new Controller::Impl( nullptr, nullptr, nullptr ); } Controller::Controller( ControlInterface* controlInterface ) { - mImpl = new Controller::Impl( controlInterface, NULL ); + mImpl = new Controller::Impl( controlInterface, NULL, NULL ); } Controller::Controller( ControlInterface* controlInterface, - EditableControlInterface* editableControlInterface ) + EditableControlInterface* editableControlInterface, + SelectableControlInterface* selectableControlInterface ) { mImpl = new Controller::Impl( controlInterface, - editableControlInterface ); + editableControlInterface, + selectableControlInterface ); } // The copy constructor and operator are left unimplemented.