// 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/text-control-interface.h>
#include <dali-toolkit/internal/text/text-run-container.h>
+using namespace Dali;
+
namespace
{
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
+};
+
} // namespace
namespace Dali
namespace Text
{
-EventData::EventData( DecoratorPtr decorator )
+EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodContext )
: mDecorator( decorator ),
- mImfManager(),
+ mInputMethodContext( inputMethodContext ),
mPlaceholderFont( NULL ),
mPlaceholderTextActive(),
mPlaceholderTextInactive(),
mAllTextSelected( false ),
mUpdateInputStyle( false ),
mPasswordInput( false ),
+ mCheckScrollAmount( false ),
mIsPlaceholderPixelSize( false ),
mIsPlaceholderElideEnabled( false ),
- mPlaceholderEllipsisFlag( false )
+ mPlaceholderEllipsisFlag( false ),
+ mShiftSelectionFlag( true ),
+ mUpdateAlignment( false )
{
- mImfManager = ImfManager::Get();
}
EventData::~EventData()
if( mEventData->mUpdateCursorPosition ||
mEventData->mUpdateHighlightBox )
{
- NotifyImfManager();
+ NotifyInputMethodContext();
}
// The cursor must also be repositioned after inserts into the model
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::PreeditStyle type = mEventData->mInputMethodContext.GetPreeditStyle();
+
+ switch( type )
+ {
+ case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
+ {
+ // 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;
+
+ mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
+ break;
+ }
+ // TODO : At this moment, other styles for preedit are not implemented yet.
+ case Dali::InputMethodContext::PreeditStyle::REVERSE:
+ case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
+ case Dali::InputMethodContext::PreeditStyle::NONE:
+ default:
+ break;
+ }
+ }
+
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 )
+ if( NULL == mEventData || !IsShowingRealText() )
{
// Nothing to do if there is no text input.
return;
{
if( mEventData->mPrimaryCursorPosition > 0u )
{
- mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
+ if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
+ {
+ mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
+ }
+ else
+ {
+ 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 );
+ if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() )
+ {
+ mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
+ }
+ else
+ {
+ mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
+ }
}
}
else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier )
mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
}
- if ( isShiftModifier && IsShowingRealText() )
+ if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag )
{
// Handle text selection
bool selecting = false;
if ( selecting )
{
- // Notify the cursor position to the imf manager.
- if( mEventData->mImfManager )
+ // Notify the cursor position to the InputMethodContext.
+ if( mEventData->mInputMethodContext )
{
- mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
- mEventData->mImfManager.NotifyCursorPosition();
+ mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
+ mEventData->mInputMethodContext.NotifyCursorPosition();
}
ChangeState( EventData::SELECTING );
mEventData->mScrollAfterUpdatePosition = true;
mEventData->mUpdateInputStyle = true;
- // Notify the cursor position to the imf manager.
- if( mEventData->mImfManager )
+ // Notify the cursor position to the InputMethodContext.
+ if( mEventData->mInputMethodContext )
{
- mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
- mEventData->mImfManager.NotifyCursorPosition();
+ mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition );
+ mEventData->mInputMethodContext.NotifyCursorPosition();
}
}
else if( 2u == tapCount )
if( mEventData->mSelectionEnabled )
{
- ChangeState( EventData::SELECTING );
+ // Calculates the logical position from the start.
+ RepositionSelectionHandles( 0.f - mModel->mScrollPosition.x,
+ 0.f - mModel->mScrollPosition.y,
+ Controller::NoTextTap::HIGHLIGHT );
mEventData->mLeftSelectionPosition = 0u;
mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
-
- mEventData->mScrollAfterUpdatePosition = true;
- mEventData->mUpdateLeftSelectionPosition = true;
- mEventData->mUpdateRightSelectionPosition = true;
- mEventData->mUpdateHighlightBox = true;
}
}
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 )
{
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 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 Text::HorizontalAlignment::CENTER:
}
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();
+
+ 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 ) ? Color::TRANSPARENT : *( 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;
+
+ PropertyBuffer quadVertices = PropertyBuffer::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.SetName( "TextBackgroundColorActor" );
+ actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
+ actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
+ actor.SetSize( textSize );
+ actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
+ actor.AddRenderer( renderer );
+ }
+ }
+
+ return actor;
+}
+
} // namespace Text
} // namespace Toolkit