/*
- * 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.
#include <dali-toolkit/internal/text/text-controller-impl.h>
// EXTERNAL INCLUDES
-#include <dali/public-api/adaptor-framework/key.h>
+#include <dali/public-api/rendering/renderer.h>
#include <dali/integration-api/debug.h>
#include <limits>
// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
#include <dali-toolkit/internal/text/bidirectional-support.h>
#include <dali-toolkit/internal/text/character-set-conversion.h>
#include <dali-toolkit/internal/text/color-segmentation.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-controller-impl-event-handler.h>
#include <dali-toolkit/internal/text/text-run-container.h>
+#include <dali-toolkit/internal/text/text-editable-control-interface.h>
+
+using namespace Dali;
namespace
{
};
#if defined(DEBUG_ENABLED)
- Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, 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 float MIN_FLOAT = std::numeric_limits<float>::min();
const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
+#define MAKE_SHADER(A)#A
+
+const char* VERTEX_SHADER_BACKGROUND = MAKE_SHADER(
+attribute mediump vec2 aPosition;
+attribute mediump vec4 aColor;
+varying mediump vec4 vColor;
+uniform highp mat4 uMvpMatrix;
+
+void main()
+{
+ mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
+ gl_Position = uMvpMatrix * position;
+ vColor = aColor;
+}
+);
+
+const char* FRAGMENT_SHADER_BACKGROUND = MAKE_SHADER(
+varying mediump vec4 vColor;
+uniform lowp vec4 uColor;
+
+void main()
+{
+ gl_FragColor = vColor * uColor;
+}
+);
+
+struct BackgroundVertex
+{
+ Vector2 mPosition; ///< Vertex posiiton
+ Vector4 mColor; ///< Vertex color
+};
+
+struct BackgroundMesh
+{
+ Vector< BackgroundVertex > mVertices; ///< container of vertices
+ Vector< unsigned short > mIndices; ///< container of indices
+};
+
+const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f );
+const Dali::Vector4 BACKGROUND_SUB4( 0.58f, 0.87f, 0.96f, 1.f );
+const Dali::Vector4 BACKGROUND_SUB5( 0.83f, 0.94f, 0.98f, 1.f );
+const Dali::Vector4 BACKGROUND_SUB6( 1.f, 0.5f, 0.5f, 1.f );
+const Dali::Vector4 BACKGROUND_SUB7( 1.f, 0.8f, 0.8f, 1.f );
+
} // namespace
namespace Dali
namespace Text
{
-EventData::EventData( DecoratorPtr decorator )
+EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodContext )
: mDecorator( decorator ),
- mImfManager(),
+ mInputMethodContext( inputMethodContext ),
mPlaceholderFont( NULL ),
mPlaceholderTextActive(),
mPlaceholderTextInactive(),
- mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
+ mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h).
mEventQueue(),
mInputStyleChangedQueue(),
mPreviousState( INACTIVE ),
mAllTextSelected( false ),
mUpdateInputStyle( false ),
mPasswordInput( false ),
+ mCheckScrollAmount( false ),
mIsPlaceholderPixelSize( false ),
mIsPlaceholderElideEnabled( false ),
- mPlaceholderEllipsisFlag( false )
+ mPlaceholderEllipsisFlag( false ),
+ mShiftSelectionFlag( true ),
+ mUpdateAlignment( false ),
+ mEditingEnabled( true )
{
- mImfManager = ImfManager::Get();
}
EventData::~EventData()
OnSelectAllEvent();
break;
}
+ case Event::SELECT_NONE:
+ {
+ OnSelectNoneEvent();
+ break;
+ }
}
}
}
if( mEventData->mUpdateCursorPosition ||
mEventData->mUpdateHighlightBox )
{
- NotifyImfManager();
+ NotifyInputMethodContext();
}
// The cursor must also be repositioned after inserts into the model
GetCursorPosition( mEventData->mPrimaryCursorPosition,
cursorInfo );
+ if( NULL != mEditableControlInterface )
+ {
+ mEditableControlInterface->CaretMoved( mEventData->mPrimaryCursorPosition );
+ }
+
if( mEventData->mUpdateCursorHookPosition )
{
// Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
return decoratorUpdated;
}
-void Controller::Impl::NotifyImfManager()
+void Controller::Impl::NotifyInputMethodContext()
{
- if( mEventData && mEventData->mImfManager )
+ if( mEventData && mEventData->mInputMethodContext )
{
CharacterIndex cursorPosition = GetLogicalCursorPosition();
cursorPosition -= numberOfWhiteSpaces;
}
- mEventData->mImfManager.SetCursorPosition( cursorPosition );
- mEventData->mImfManager.NotifyCursorPosition();
+ mEventData->mInputMethodContext.SetCursorPosition( cursorPosition );
+ mEventData->mInputMethodContext.NotifyCursorPosition();
}
}
-void Controller::Impl::NotifyImfMultiLineStatus()
+void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
{
- if ( mEventData )
+ if ( mEventData && mEventData->mInputMethodContext )
{
Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
- mEventData->mImfManager.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
+ mEventData->mInputMethodContext.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
}
}
mModel->mLogicalModel->mParagraphInfo.Clear();
}
- if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
- {
- mModel->mLogicalModel->mLineBreakInfo.Clear();
- }
-
if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
{
mModel->mLogicalModel->mScriptRuns.Clear();
if( NO_OPERATION != ( COLOR & operations ) )
{
mModel->mVisualModel->mColorIndices.Clear();
+ mModel->mVisualModel->mBackgroundColorIndices.Clear();
}
}
mModel->mLogicalModel->mParagraphInfo );
}
- if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
- {
- // Clear the word break info.
- WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
-
- mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
- wordBreakInfoBuffer + endIndexPlusOne );
- }
-
if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
{
// Clear the scripts.
mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
colorIndexBuffer + endGlyphIndexPlusOne );
}
+
+ if( 0u != mModel->mVisualModel->mBackgroundColorIndices.Count() )
+ {
+ ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
+ mModel->mVisualModel->mBackgroundColorIndices.Erase( backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
+ backgroundColorIndexBuffer + endGlyphIndexPlusOne );
+ }
}
}
Length paragraphCharacters = 0u;
CalculateTextUpdateIndices( paragraphCharacters );
+
+ // Check whether the indices for updating the text is valid
+ if ( numberOfCharacters > 0u &&
+ ( mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
+ mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) )
+ {
+ std::string currentText;
+ Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText );
+
+ DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" );
+ DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() );
+
+ // Dump mTextUpdateInfo
+ DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" );
+ DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex );
+ DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove );
+ DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd );
+ DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters );
+ DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex );
+ DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters );
+ DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex );
+ DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex );
+ DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines );
+ DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll );
+ DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded );
+ DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph );
+
+ return false;
+ }
+
startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
if( mTextUpdateInfo.mClearAll ||
updated = true;
}
- Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
- if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
- {
- // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
- wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
-
- SetWordBreakInfo( utf32Characters,
- startIndex,
- requestedNumberOfCharacters,
- wordBreakInfo );
- updated = true;
- }
-
const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
TextAbstraction::FontDescription defaultFontDescription;
TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
- if( IsShowingPlaceholderText() && ( NULL != mEventData->mPlaceholderFont ) )
+ if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) )
{
// If the placeholder font is set specifically, only placeholder font is changed.
defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
{
// Set the normal font and the placeholder font.
defaultFontDescription = mFontDefaults->mFontDescription;
- defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
+
+ if( mTextFitEnabled )
+ {
+ defaultPointSize = mFontDefaults->mFitPointSize * 64u;
+ }
+ else
+ {
+ defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
+ }
}
// Validates the fonts. If there is a character with no assigned font it sets a default one.
lineBreakInfo,
startIndex,
requestedNumberOfCharacters,
- bidirectionalInfo );
+ bidirectionalInfo,
+ mModel->mMatchSystemLanguageDirection,
+ mLayoutDirection );
if( 0u != bidirectionalInfo.Count() )
{
updated = true;
}
+ if( ( NULL != mEventData ) &&
+ mEventData->mPreEditFlag &&
+ ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
+ {
+ Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
+ mEventData->mInputMethodContext.GetPreeditStyle( attrs );
+ Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
+
+ // Check the type of preedit and run it.
+ for( Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++ )
+ {
+ Dali::InputMethodContext::PreeditAttributeData attrData = *it;
+ DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex );
+ type = attrData.preeditType;
+
+ // Check the number of commit characters for the start position.
+ unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
+ Length numberOfIndices = attrData.endIndex - attrData.startIndex;
+
+ switch( type )
+ {
+ case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
+ {
+ // Add the underline for the pre-edit text.
+ GlyphRun underlineRun;
+ underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
+ underlineRun.numberOfGlyphs = numberOfIndices;
+ mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
+ break;
+ }
+ case Dali::InputMethodContext::PreeditStyle::REVERSE:
+ {
+ Vector4 textColor = mModel->mVisualModel->GetTextColor();
+ ColorRun backgroundColorRun;
+ backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
+ backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
+ backgroundColorRun.color = textColor;
+ mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
+
+ Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
+ Vector<ColorRun> colorRuns;
+ colorRuns.Resize( 1u );
+ ColorRun& colorRun = *( colorRuns.Begin() );
+ colorRun.color = backgroundColor;
+ colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
+ colorRun.characterRun.numberOfCharacters = numberOfIndices;
+
+ mModel->mLogicalModel->mColorRuns.PushBack( colorRun );
+ break;
+ }
+ case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
+ {
+ ColorRun backgroundColorRun;
+ backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
+ backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
+ backgroundColorRun.color = LIGHT_BLUE;
+ mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
+ break;
+ }
+ case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
+ {
+ // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
+ ColorRun backgroundColorRun;
+ backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
+ backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
+ backgroundColorRun.color = BACKGROUND_SUB4;
+ mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
+
+ GlyphRun underlineRun;
+ underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
+ underlineRun.numberOfGlyphs = numberOfIndices;
+ mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
+ break;
+ }
+ case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
+ {
+ // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
+ ColorRun backgroundColorRun;
+ backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
+ backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
+ backgroundColorRun.color = BACKGROUND_SUB5;
+ mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
+
+ GlyphRun underlineRun;
+ underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
+ underlineRun.numberOfGlyphs = numberOfIndices;
+ mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
+ break;
+ }
+ case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
+ {
+ // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
+ ColorRun backgroundColorRun;
+ backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
+ backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
+ backgroundColorRun.color = BACKGROUND_SUB6;
+ mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
+
+ GlyphRun underlineRun;
+ underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
+ underlineRun.numberOfGlyphs = numberOfIndices;
+ mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
+ break;
+ }
+ case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
+ {
+ // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
+ ColorRun backgroundColorRun;
+ backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
+ backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
+ backgroundColorRun.color = BACKGROUND_SUB7;
+ mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun );
+
+ GlyphRun underlineRun;
+ underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
+ underlineRun.numberOfGlyphs = numberOfIndices;
+ mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
+ break;
+ }
+ case Dali::InputMethodContext::PreeditStyle::NONE:
+ default:
+ {
+ break;
+ }
+ }
+ }
+ attrs.Clear();
+ updated = true;
+ }
+
if( NO_OPERATION != ( COLOR & operations ) )
{
// Set the color runs in glyphs.
mModel->mVisualModel->mColors,
mModel->mVisualModel->mColorIndices );
+ // Set the background color runs in glyphs.
+ SetColorSegmentationInfo( mModel->mLogicalModel->mBackgroundColorRuns,
+ mModel->mVisualModel->mCharactersToGlyph,
+ mModel->mVisualModel->mGlyphsPerCharacter,
+ startIndex,
+ mTextUpdateInfo.mStartGlyphIndex,
+ requestedNumberOfCharacters,
+ mModel->mVisualModel->mBackgroundColors,
+ mModel->mVisualModel->mBackgroundColorIndices );
+
updated = true;
}
- if( ( NULL != mEventData ) &&
- mEventData->mPreEditFlag &&
- ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
- {
- // Add the underline for the pre-edit text.
- const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
- const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
-
- const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
- const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? 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.
- mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
- }
// The estimated number of lines. Used to avoid reallocations when layouting.
mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
void Controller::Impl::OnCursorKeyEvent( const Event& event )
{
- if( NULL == mEventData )
- {
- // Nothing to do if there is no text input.
- return;
- }
-
- int keyCode = event.p1.mInt;
-
- if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
- {
- if( mEventData->mPrimaryCursorPosition > 0u )
- {
- mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
- }
- }
- else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
- {
- if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
- {
- mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
- }
- }
- else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
- {
- // Get first the line index of the current cursor position index.
- CharacterIndex characterIndex = 0u;
-
- if( mEventData->mPrimaryCursorPosition > 0u )
- {
- characterIndex = mEventData->mPrimaryCursorPosition - 1u;
- }
-
- const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
- const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
-
- // Retrieve the cursor position info.
- CursorInfo cursorInfo;
- GetCursorPosition( mEventData->mPrimaryCursorPosition,
- cursorInfo );
-
- // Get the line above.
- const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
-
- // Get the next hit 'y' point.
- 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( mModel->mVisualModel,
- mModel->mLogicalModel,
- mMetrics,
- mEventData->mCursorHookPositionX,
- hitPointY,
- CharacterHitTest::TAP,
- matchedCharacter );
- }
- else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
- {
- // Get first the line index of the current cursor position index.
- CharacterIndex characterIndex = 0u;
-
- if( mEventData->mPrimaryCursorPosition > 0u )
- {
- characterIndex = mEventData->mPrimaryCursorPosition - 1u;
- }
-
- const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
-
- if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
- {
- // Retrieve the cursor position info.
- CursorInfo cursorInfo;
- GetCursorPosition( mEventData->mPrimaryCursorPosition,
- cursorInfo );
-
- // Get the line below.
- const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
-
- // Get the next hit 'y' point.
- 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( mModel->mVisualModel,
- mModel->mLogicalModel,
- mMetrics,
- mEventData->mCursorHookPositionX,
- hitPointY,
- CharacterHitTest::TAP,
- matchedCharacter );
- }
- }
-
- mEventData->mUpdateCursorPosition = true;
- mEventData->mUpdateInputStyle = true;
- mEventData->mScrollAfterUpdatePosition = true;
+ ControllerImplEventHandler::OnCursorKeyEvent(*this, event);
}
void Controller::Impl::OnTapEvent( const Event& event )
{
- if( NULL != mEventData )
- {
- const unsigned int tapCount = event.p1.mUint;
-
- if( 1u == tapCount )
- {
- if( IsShowingRealText() )
- {
- // Convert from control's coords to text's coords.
- const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
- const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
-
- // 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( mModel->mVisualModel,
- mModel->mLogicalModel,
- mMetrics,
- xPosition,
- yPosition,
- CharacterHitTest::TAP,
- matchedCharacter );
-
- // When the cursor position is changing, delay cursor blinking
- mEventData->mDecorator->DelayCursorBlink();
- }
- else
- {
- mEventData->mPrimaryCursorPosition = 0u;
- }
-
- mEventData->mUpdateCursorPosition = true;
- mEventData->mUpdateGrabHandlePosition = true;
- mEventData->mScrollAfterUpdatePosition = true;
- mEventData->mUpdateInputStyle = true;
-
- // Notify the cursor position to the imf manager.
- if( mEventData->mImfManager )
- {
- mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
- 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 - mModel->mScrollPosition.x;
- const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
-
- // Calculates the logical position from the x,y coords.
- RepositionSelectionHandles( xPosition,
- yPosition,
- mEventData->mDoubleTapAction );
- }
- }
- }
+ ControllerImplEventHandler::OnTapEvent(*this, event);
}
void Controller::Impl::OnPanEvent( const Event& event )
{
- if( NULL == mEventData )
- {
- // Nothing to do if there is no text input.
- return;
- }
-
- const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
- const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
-
- if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
- {
- // Nothing to do if scrolling is not enabled.
- return;
- }
-
- const int state = event.p1.mInt;
-
- switch( state )
- {
- case Gesture::Started:
- {
- // Will remove the cursor, handles or text's popup, ...
- ChangeState( EventData::TEXT_PANNING );
- break;
- }
- case Gesture::Continuing:
- {
- const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
- const Vector2 currentScroll = mModel->mScrollPosition;
-
- if( isHorizontalScrollEnabled )
- {
- const float displacementX = event.p2.mFloat;
- mModel->mScrollPosition.x += displacementX;
-
- ClampHorizontalScroll( layoutSize );
- }
-
- if( isVerticalScrollEnabled )
- {
- const float displacementY = event.p3.mFloat;
- mModel->mScrollPosition.y += displacementY;
+ ControllerImplEventHandler::OnPanEvent(*this, event);
+}
- ClampVerticalScroll( layoutSize );
- }
+void Controller::Impl::OnLongPressEvent( const Event& event )
+{
+ ControllerImplEventHandler::OnLongPressEvent(*this, event);
+}
- mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
- break;
- }
- case Gesture::Finished:
- case Gesture::Cancelled: // FALLTHROUGH
- {
- // Will go back to the previous state to show the cursor, handles, the text's popup, ...
- ChangeState( mEventData->mPreviousState );
- break;
- }
- default:
- break;
- }
+void Controller::Impl::OnHandleEvent( const Event& event )
+{
+ ControllerImplEventHandler::OnHandleEvent(*this, event);
}
-void Controller::Impl::OnLongPressEvent( const Event& event )
+void Controller::Impl::OnSelectEvent( const Event& event )
{
- DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
+ ControllerImplEventHandler::OnSelectEvent(*this, event);
+}
- if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) )
- {
- 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 - mModel->mScrollPosition.x;
- const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
+void Controller::Impl::OnSelectAllEvent()
+{
+ ControllerImplEventHandler::OnSelectAllEvent(*this);
+}
- // Calculates the logical position from the x,y coords.
- RepositionSelectionHandles( xPosition,
- yPosition,
- mEventData->mLongPressAction );
- }
- }
+void Controller::Impl::OnSelectNoneEvent()
+{
+ ControllerImplEventHandler::OnSelectNoneEvent(*this);
}
-void Controller::Impl::OnHandleEvent( const Event& event )
+void Controller::Impl::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd)
{
- if( NULL == mEventData )
+ if( nullptr == mEventData )
{
- // Nothing to do if there is no text input.
+ // Nothing to do if there is no text.
return;
}
- const unsigned int state = event.p1.mUint;
- const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
- const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
-
- if( HANDLE_PRESSED == state )
+ if( mEventData->mSelectionEnabled && (pStart || pEnd))
{
- // Convert from decorator's coords to text's coords.
- const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
- const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
-
- // Need to calculate the handle's new position.
- bool matchedCharacter = false;
- const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
- mModel->mLogicalModel,
- mMetrics,
- xPosition,
- yPosition,
- CharacterHitTest::SCROLL,
- matchedCharacter );
-
- if( Event::GRAB_HANDLE_EVENT == event.type )
- {
- ChangeState ( EventData::GRAB_HANDLE_PANNING );
-
- if( handleNewPosition != mEventData->mPrimaryCursorPosition )
- {
- // Updates the cursor position if the handle's new position is different than the current one.
- mEventData->mUpdateCursorPosition = true;
- // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
- mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
- mEventData->mPrimaryCursorPosition = handleNewPosition;
- }
-
- // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
- mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
- }
- else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
- {
- ChangeState ( EventData::SELECTION_HANDLE_PANNING );
+ uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
- if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
- ( handleNewPosition != mEventData->mRightSelectionPosition ) )
- {
- // Updates the highlight box if the handle's new position is different than the current one.
- mEventData->mUpdateHighlightBox = true;
- // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
- mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
- mEventData->mLeftSelectionPosition = handleNewPosition;
- }
-
- // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
- mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
-
- // 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 )
+ if (pStart)
{
- ChangeState ( EventData::SELECTION_HANDLE_PANNING );
-
- if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
- ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
- {
- // Updates the highlight box if the handle's new position is different than the current one.
- mEventData->mUpdateHighlightBox = true;
- // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
- mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
- mEventData->mRightSelectionPosition = handleNewPosition;
- }
-
- // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
- mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
-
- // Will define the order to scroll the text to match the handle position.
- mEventData->mIsLeftHandleSelected = false;
- mEventData->mIsRightHandleSelected = true;
+ mEventData->mLeftSelectionPosition = std::min(*pStart, length);
}
- } // end ( HANDLE_PRESSED == state )
- else if( ( HANDLE_RELEASED == state ) ||
- handleStopScrolling )
- {
- CharacterIndex handlePosition = 0u;
- if( handleStopScrolling || isSmoothHandlePanEnabled )
+ if (pEnd)
{
- // Convert from decorator's coords to text's coords.
- const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
- const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
-
- bool matchedCharacter = false;
- handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
- mModel->mLogicalModel,
- mMetrics,
- xPosition,
- yPosition,
- CharacterHitTest::SCROLL,
- matchedCharacter );
+ mEventData->mRightSelectionPosition = std::min(*pEnd, length);
}
- if( Event::GRAB_HANDLE_EVENT == event.type )
+ if (mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
{
+ ChangeState( EventData::EDITING );
+ mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
mEventData->mUpdateCursorPosition = true;
- mEventData->mUpdateGrabHandlePosition = true;
- mEventData->mUpdateInputStyle = true;
-
- if( !IsClipboardEmpty() )
- {
- ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
- }
-
- if( handleStopScrolling || isSmoothHandlePanEnabled )
- {
- mEventData->mScrollAfterUpdatePosition = true;
- mEventData->mPrimaryCursorPosition = handlePosition;
- }
}
- else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
+ else
{
ChangeState( EventData::SELECTING );
-
mEventData->mUpdateHighlightBox = true;
mEventData->mUpdateLeftSelectionPosition = true;
mEventData->mUpdateRightSelectionPosition = true;
-
- if( handleStopScrolling || isSmoothHandlePanEnabled )
- {
- mEventData->mScrollAfterUpdatePosition = true;
-
- if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
- ( handlePosition != mEventData->mLeftSelectionPosition ) )
- {
- mEventData->mLeftSelectionPosition = handlePosition;
- }
- }
- }
- else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
- {
- ChangeState( EventData::SELECTING );
-
- mEventData->mUpdateHighlightBox = true;
- mEventData->mUpdateRightSelectionPosition = true;
- mEventData->mUpdateLeftSelectionPosition = true;
-
- if( handleStopScrolling || isSmoothHandlePanEnabled )
- {
- mEventData->mScrollAfterUpdatePosition = true;
- if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
- ( handlePosition != mEventData->mLeftSelectionPosition ) )
- {
- mEventData->mRightSelectionPosition = handlePosition;
- }
- }
- }
-
- mEventData->mDecoratorUpdated = true;
- } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
- else if( HANDLE_SCROLLING == state )
- {
- const float xSpeed = event.p2.mFloat;
- const float ySpeed = event.p3.mFloat;
- const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
- const Vector2 currentScrollPosition = mModel->mScrollPosition;
-
- mModel->mScrollPosition.x += xSpeed;
- mModel->mScrollPosition.y += ySpeed;
-
- ClampHorizontalScroll( layoutSize );
- ClampVerticalScroll( layoutSize );
-
- bool endOfScroll = false;
- if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
- {
- // Notify the decorator there is no more text to scroll.
- // The decorator won't send more scroll events.
- mEventData->mDecorator->NotifyEndOfScroll();
- // Still need to set the position of the handle.
- endOfScroll = true;
- }
-
- // Set the position of the handle.
- const bool scrollRightDirection = xSpeed > 0.f;
- const bool scrollBottomDirection = ySpeed > 0.f;
- const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
- const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
-
- if( Event::GRAB_HANDLE_EVENT == event.type )
- {
- ChangeState( EventData::GRAB_HANDLE_PANNING );
-
- // Get the grab handle position in decorator coords.
- Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
-
- if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
- {
- // Position the grag handle close to either the left or right edge.
- position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
- }
-
- if( mEventData->mDecorator->IsVerticalScrollEnabled() )
- {
- position.x = mEventData->mCursorHookPositionX;
-
- // Position the grag handle close to either the top or bottom edge.
- position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
- }
-
- // 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( mModel->mVisualModel,
- mModel->mLogicalModel,
- mMetrics,
- position.x - mModel->mScrollPosition.x,
- position.y - mModel->mScrollPosition.y,
- CharacterHitTest::SCROLL,
- matchedCharacter );
-
- if( mEventData->mPrimaryCursorPosition != handlePosition )
- {
- mEventData->mUpdateCursorPosition = true;
- mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
- mEventData->mScrollAfterUpdatePosition = true;
- mEventData->mPrimaryCursorPosition = handlePosition;
- }
- mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
-
- // Updates the decorator if the soft handle panning is enabled.
- mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
}
- else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
- {
- ChangeState( EventData::SELECTION_HANDLE_PANNING );
-
- // Get the selection handle position in decorator coords.
- Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
-
- if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
- {
- // Position the selection handle close to either the left or right edge.
- position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
- }
-
- if( mEventData->mDecorator->IsVerticalScrollEnabled() )
- {
- position.x = mEventData->mCursorHookPositionX;
-
- // Position the grag handle close to either the top or bottom edge.
- position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
- }
-
- // 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( mModel->mVisualModel,
- mModel->mLogicalModel,
- mMetrics,
- position.x - mModel->mScrollPosition.x,
- position.y - mModel->mScrollPosition.y,
- CharacterHitTest::SCROLL,
- matchedCharacter );
-
- if( leftSelectionHandleEvent )
- {
- const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
-
- if( differentHandles || endOfScroll )
- {
- mEventData->mUpdateHighlightBox = true;
- mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
- mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
- mEventData->mLeftSelectionPosition = handlePosition;
- }
- }
- else
- {
- const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
- if( differentHandles || endOfScroll )
- {
- mEventData->mUpdateHighlightBox = true;
- mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
- mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
- mEventData->mRightSelectionPosition = handlePosition;
- }
- }
-
- if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
- {
- RepositionSelectionHandles();
-
- mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
- }
- }
- mEventData->mDecoratorUpdated = true;
- } // end ( HANDLE_SCROLLING == state )
+ }
}
-void Controller::Impl::OnSelectEvent( const Event& event )
+Uint32Pair Controller::Impl::GetTextSelectionRange() const
{
- if( NULL == mEventData )
- {
- // Nothing to do if there is no text.
- return;
- }
+ Uint32Pair range;
- if( mEventData->mSelectionEnabled )
+ if( mEventData )
{
- // Convert from control's coords to text's coords.
- const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
- const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
-
- // Calculates the logical position from the x,y coords.
- RepositionSelectionHandles( xPosition,
- yPosition,
- Controller::NoTextTap::HIGHLIGHT );
+ range.first = mEventData->mLeftSelectionPosition;
+ range.second = mEventData->mRightSelectionPosition;
}
+
+ return range;
}
-void Controller::Impl::OnSelectAllEvent()
+bool Controller::Impl::IsEditable() const
{
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
-
- if( NULL == mEventData )
- {
- // Nothing to do if there is no text.
- return;
- }
+ return mEventData && mEventData->mEditingEnabled;
+}
- if( mEventData->mSelectionEnabled )
+void Controller::Impl::SetEditable( bool editable )
+{
+ if( mEventData)
{
- ChangeState( EventData::SELECTING );
-
- mEventData->mLeftSelectionPosition = 0u;
- mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
-
- mEventData->mScrollAfterUpdatePosition = true;
- mEventData->mUpdateLeftSelectionPosition = true;
- mEventData->mUpdateRightSelectionPosition = true;
- mEventData->mUpdateHighlightBox = true;
+ mEventData->mEditingEnabled = editable;
}
}
}
}
+void Controller::Impl::SetSelection( int start, int end )
+{
+ mEventData->mLeftSelectionPosition = start;
+ mEventData->mRightSelectionPosition = end;
+ mEventData->mUpdateCursorPosition = true;
+}
+
+std::pair< int, int > Controller::Impl::GetSelectionIndexes() const
+{
+ return { mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition };
+}
+
void Controller::Impl::ShowClipboard()
{
if( mClipboard )
mClipboardHideEnabled = enable;
}
-bool Controller::Impl::CopyStringToClipboard( std::string& source )
+bool Controller::Impl::CopyStringToClipboard( const std::string& source )
{
//Send string to clipboard
return ( mClipboard && mClipboard.SetItem( source ) );
if( selectionStart == selectionEnd )
{
// Nothing to select if handles are in the same place.
+ // So, deactive Highlight box.
+ mEventData->mDecorator->SetHighlightActive( false );
return;
}
const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
- mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
+ mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast<float>( mModel->GetOutlineWidth() ) );
if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
{
secondaryCursorInfo.lineHeight );
}
- // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
- mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
-
// Set the flag to update the decorator.
mEventData->mDecoratorUpdated = true;
}
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
+ // It may happen an InputMethodContext commit event arrives before the selection event
+ // if the InputMethodContext 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 );
+
+ // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
+ mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
}
else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action )
{
* If EDITING_WITH_POPUP : SELECT & SELECT_ALL
*/
+ bool isEditable = IsEditable();
TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
if( EventData::SELECTING == mEventData->mState )
{
- buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY );
+ buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::COPY );
+ if(isEditable)
+ {
+ buttonsToShow = TextSelectionPopup::Buttons( buttonsToShow | TextSelectionPopup::CUT );
+ }
if( !IsClipboardEmpty() )
{
- buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
+ if(isEditable)
+ {
+ buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
+ }
buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
}
if( !IsClipboardEmpty() )
{
- buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
+ if(isEditable)
+ {
+ buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
+ }
buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
}
}
{
if ( !IsClipboardEmpty() )
{
- buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
+ if(isEditable)
+ {
+ buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
+ }
buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
}
}
mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
mEventData->mDecorator->StopCursorBlink();
mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
- mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
- mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
+ if ( mEventData->mGrabHandleEnabled )
+ {
+ mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
+ mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
+ }
mEventData->mDecorator->SetHighlightActive( true );
if( mEventData->mGrabHandlePopupEnabled )
{
mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
mEventData->mDecorator->SetHighlightActive( false );
}
- else
+ else if ( mEventData->mGrabHandleEnabled )
{
mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
}
mEventData->mDecorator->StartCursorBlink();
}
// Grab handle is not shown until a tap is received whilst EDITING
- mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
+ if ( mEventData->mGrabHandleEnabled )
+ {
+ mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
+ }
mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
mEventData->mDecorator->SetHighlightActive( false );
mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
mEventData->mDecorator->StopCursorBlink();
mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
- mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
- mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
+ if ( mEventData->mGrabHandleEnabled )
+ {
+ mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
+ mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
+ }
mEventData->mDecorator->SetHighlightActive( true );
if( mEventData->mGrabHandlePopupEnabled )
{
{
mEventData->mDecorator->StartCursorBlink();
}
- mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
+ if ( mEventData->mGrabHandleEnabled )
+ {
+ mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
+ }
mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
mEventData->mDecorator->SetHighlightActive( false );
mEventData->mDecorator->StartCursorBlink();
}
- mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
+ if ( mEventData->mGrabHandleEnabled )
+ {
+ mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
+ }
mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
mEventData->mDecorator->SetHighlightActive( false );
cursorInfo.lineHeight = GetDefaultFontLineHeight();
cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
+ bool isRTL = false;
+ if( mModel->mMatchSystemLanguageDirection )
+ {
+ isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
+ }
+
switch( mModel->mHorizontalAlignment )
{
- case Layout::HORIZONTAL_ALIGN_BEGIN:
+ case Text::HorizontalAlignment::BEGIN :
{
- cursorInfo.primaryPosition.x = 0.f;
+ if( isRTL )
+ {
+ cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
+ }
+ else
+ {
+ cursorInfo.primaryPosition.x = 0.f;
+ }
break;
}
- case Layout::HORIZONTAL_ALIGN_CENTER:
+ case Text::HorizontalAlignment::CENTER:
{
cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
break;
}
- case Layout::HORIZONTAL_ALIGN_END:
+ case Text::HorizontalAlignment::END:
{
- cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
+ if( isRTL )
+ {
+ cursorInfo.primaryPosition.x = 0.f;
+ }
+ else
+ {
+ cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
+ }
break;
}
}
Text::GetCursorPosition( parameters,
cursorInfo );
+ // Adds Outline offset.
+ const float outlineWidth = static_cast<float>( mModel->GetOutlineWidth() );
+ cursorInfo.primaryPosition.x += outlineWidth;
+ cursorInfo.primaryPosition.y += outlineWidth;
+ cursorInfo.secondaryPosition.x += outlineWidth;
+ cursorInfo.secondaryPosition.y += outlineWidth;
+
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.
}
}
+Actor Controller::Impl::CreateBackgroundActor()
+{
+ // NOTE: Currently we only support background color for one line left-to-right text,
+ // so the following calculation is based on one line left-to-right text only!
+
+ Actor actor;
+
+ Length numberOfGlyphs = mView.GetNumberOfGlyphs();
+ if( numberOfGlyphs > 0u )
+ {
+ Vector<GlyphInfo> glyphs;
+ glyphs.Resize( numberOfGlyphs );
+
+ Vector<Vector2> positions;
+ positions.Resize( numberOfGlyphs );
+
+ // Get the line where the glyphs are laid-out.
+ const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
+ float alignmentOffset = lineRun->alignmentOffset;
+ numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(),
+ positions.Begin(),
+ alignmentOffset,
+ 0u,
+ numberOfGlyphs );
+
+ glyphs.Resize( numberOfGlyphs );
+ positions.Resize( numberOfGlyphs );
+
+ const GlyphInfo* const glyphsBuffer = glyphs.Begin();
+ const Vector2* const positionsBuffer = positions.Begin();
+
+ BackgroundMesh mesh;
+ mesh.mVertices.Reserve( 4u * glyphs.Size() );
+ mesh.mIndices.Reserve( 6u * glyphs.Size() );
+
+ const Vector2 textSize = mView.GetLayoutSize();
+
+ const float offsetX = textSize.width * 0.5f;
+ const float offsetY = textSize.height * 0.5f;
+
+ const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
+ const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
+ const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
+
+ Vector4 quad;
+ uint32_t numberOfQuads = 0u;
+
+ for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i )
+ {
+ const GlyphInfo& glyph = *( glyphsBuffer + i );
+
+ // Get the background color of the character.
+ // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
+ const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i );
+ const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? defaultBackgroundColor : *( backgroundColorsBuffer + backgroundColorIndex - 1u );
+
+ // Only create quads for glyphs with a background color
+ if ( backgroundColor != Color::TRANSPARENT )
+ {
+ const Vector2 position = *( positionsBuffer + i );
+
+ if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text
+ {
+ quad.x = position.x;
+ quad.y = 0.0f;
+ quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
+ quad.w = textSize.height;
+ }
+ else if ( i == 0u ) // The first glyph in the whole text
+ {
+ quad.x = position.x;
+ quad.y = 0.0f;
+ quad.z = quad.x - glyph.xBearing + glyph.advance;
+ quad.w = textSize.height;
+ }
+ else if ( i == glyphSize - 1u ) // The last glyph in the whole text
+ {
+ quad.x = position.x - glyph.xBearing;
+ quad.y = 0.0f;
+ quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width );
+ quad.w = textSize.height;
+ }
+ else // The glyph in the middle of the text
+ {
+ quad.x = position.x - glyph.xBearing;
+ quad.y = 0.0f;
+ quad.z = quad.x + glyph.advance;
+ quad.w = textSize.height;
+ }
+
+ BackgroundVertex vertex;
+
+ // Top left
+ vertex.mPosition.x = quad.x - offsetX;
+ vertex.mPosition.y = quad.y - offsetY;
+ vertex.mColor = backgroundColor;
+ mesh.mVertices.PushBack( vertex );
+
+ // Top right
+ vertex.mPosition.x = quad.z - offsetX;
+ vertex.mPosition.y = quad.y - offsetY;
+ vertex.mColor = backgroundColor;
+ mesh.mVertices.PushBack( vertex );
+
+ // Bottom left
+ vertex.mPosition.x = quad.x - offsetX;
+ vertex.mPosition.y = quad.w - offsetY;
+ vertex.mColor = backgroundColor;
+ mesh.mVertices.PushBack( vertex );
+
+ // Bottom right
+ vertex.mPosition.x = quad.z - offsetX;
+ vertex.mPosition.y = quad.w - offsetY;
+ vertex.mColor = backgroundColor;
+ mesh.mVertices.PushBack( vertex );
+
+ // Six indices in counter clockwise winding
+ mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
+ mesh.mIndices.PushBack( 0u + 4 * numberOfQuads );
+ mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
+ mesh.mIndices.PushBack( 2u + 4 * numberOfQuads );
+ mesh.mIndices.PushBack( 3u + 4 * numberOfQuads );
+ mesh.mIndices.PushBack( 1u + 4 * numberOfQuads );
+
+ numberOfQuads++;
+ }
+ }
+
+ // Only create the background actor if there are glyphs with background color
+ if ( mesh.mVertices.Count() > 0u )
+ {
+ Property::Map quadVertexFormat;
+ quadVertexFormat[ "aPosition" ] = Property::VECTOR2;
+ quadVertexFormat[ "aColor" ] = Property::VECTOR4;
+
+ VertexBuffer quadVertices = VertexBuffer::New( quadVertexFormat );
+ quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() );
+
+ Geometry quadGeometry = Geometry::New();
+ quadGeometry.AddVertexBuffer( quadVertices );
+ quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() );
+
+ if( !mShaderBackground )
+ {
+ mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND );
+ }
+
+ Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground );
+ renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON );
+ renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT );
+
+ actor = Actor::New();
+ actor.SetProperty( Dali::Actor::Property::NAME, "TextBackgroundColorActor" );
+ actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
+ actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
+ actor.SetProperty( Actor::Property::SIZE, textSize );
+ actor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR );
+ actor.AddRenderer( renderer );
+ }
+ }
+
+ return actor;
+}
+
} // namespace Text
} // namespace Toolkit