// INTERNAL INCLUDES
#include <dali-toolkit/internal/text/bidirectional-support.h>
#include <dali-toolkit/internal/text/character-set-conversion.h>
-#include <dali-toolkit/internal/text/layouts/layout-parameters.h>
#include <dali-toolkit/internal/text/multi-language-support.h>
-#include <dali-toolkit/internal/text/script-run.h>
#include <dali-toolkit/internal/text/segmentation.h>
#include <dali-toolkit/internal/text/shaper.h>
-#include <dali-toolkit/internal/text/text-io.h>
-#include <dali-toolkit/internal/text/text-view.h>
namespace
{
{
// Retrieves the scripts used in the text.
multilanguageSupport.SetScripts( utf32Characters,
- lineBreakInfo,
scripts );
}
Vector<Character> mirroredUtf32Characters;
bool textMirrored = false;
+ Length numberOfParagraphs = 0u;
if( BIDI_INFO & operations )
{
// Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
// bidirectional info.
- Length numberOfParagraphs = 0u;
-
const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
for( Length index = 0u; index < numberOfCharacters; ++index )
{
// This paragraph has right to left text. Some characters may need to be mirrored.
// TODO: consider if the mirrored string can be stored as well.
- textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
+ textMirrored = GetMirroredText( utf32Characters,
+ mirroredUtf32Characters,
+ bidirectionalInfo );
// Only set the character directions if there is right to left characters.
Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
+ Vector<GlyphIndex> newParagraphGlyphs;
+ newParagraphGlyphs.Reserve( numberOfParagraphs );
+
if( SHAPE_TEXT & operations )
{
const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
validFonts,
glyphs,
glyphsToCharactersMap,
- charactersPerGlyph );
+ charactersPerGlyph,
+ newParagraphGlyphs );
// Create the 'number of glyphs' per character and the glyph to character conversion tables.
mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
if( GET_GLYPH_METRICS & operations )
{
- mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
+ GlyphInfo* glyphsBuffer = glyphs.Begin();
+ mFontClient.GetGlyphMetrics( glyphsBuffer, numberOfGlyphs );
+
+ // Update the width and advance of all new paragraph characters.
+ for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
+ {
+ const GlyphIndex index = *it;
+ GlyphInfo& glyph = *( glyphsBuffer + index );
+
+ glyph.xBearing = 0.f;
+ glyph.width = 0.f;
+ glyph.advance = 0.f;
+ }
+ }
+
+ if( mEventData &&
+ mEventData->mPreEditFlag &&
+ ( 0u != mVisualModel->mCharactersToGlyph.Count() ) )
+ {
+ // Add the underline for the pre-edit text.
+ const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
+ const Length* const glyphsPerCharacterBuffer = 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.
+ mVisualModel->mUnderlineRuns.PushBack( underlineRun );
}
}
if( 1u == tapCount )
{
- if( ! IsShowingPlaceholderText() )
+ if( IsShowingRealText() )
{
const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
yPosition );
+
+ // When the cursor position is changing, delay cursor blinking
+ mEventData->mDecorator->DelayCursorBlink();
}
else
{
mEventData->mDecorator->AddHighlight( xPosition,
offset.y,
xPosition + static_cast<float>( numberOfCharacters ) * glyphAdvance,
- height );
+ offset.y + height );
splitStartGlyph = false;
continue;
mEventData->mDecorator->AddHighlight( xPosition,
offset.y,
xPosition + static_cast<float>( interGlyphIndex ) * glyphAdvance,
- height );
+ offset.y + height );
splitEndGlyph = false;
continue;
}
const float xPosition = position.x - glyph.xBearing + offset.x;
- mEventData->mDecorator->AddHighlight( xPosition, offset.y, xPosition + glyph.advance, height );
+ mEventData->mDecorator->AddHighlight( xPosition,
+ offset.y,
+ xPosition + glyph.advance,
+ offset.y + height );
}
CursorInfo primaryCursorInfo;
mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, 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;
}
void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
{
CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
+ DALI_ASSERT_DEBUG( hitCharacter <= mLogicalModel->mText.Count() && "GetClosestCursorIndex returned out of bounds index" );
+
+ if ( mLogicalModel->mText.Count() == 0 )
+ {
+ return; // if model empty
+ }
+
if( hitCharacter >= mLogicalModel->mText.Count() )
{
- // Selection out of bounds.
- return;
+ // Closest hit character is the last character.
+ if ( hitCharacter == mLogicalModel->mText.Count() )
+ {
+ hitCharacter--; //Hit character index set to last character in logical model
+ }
+ else
+ {
+ // hitCharacter is out of bounds
+ return;
+ }
}
startIndex = hitCharacter;
CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
float visualY )
{
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetClosestCursorIndex %p closest visualX %f visualY %f\n", this, visualX, visualY );
+
if( NULL == mEventData )
{
// Nothing to do if there is no text input.
logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
+ DALI_ASSERT_DEBUG( ( logicalIndex <= mLogicalModel->mText.Count() && logicalIndex >= 0 ) && "GetClosestCursorIndex - Out of bounds index" );
+
return logicalIndex;
}
cursorInfo.lineHeight = GetDefaultFontLineHeight();
cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
- cursorInfo.primaryPosition.x = 1.f;
+ cursorInfo.primaryPosition.x = mEventData->mDecorator->GetCursorWidth();
cursorInfo.primaryPosition.y = 0.f;
// Nothing else to do.
return;
}
- if( IsShowingPlaceholderText() )
+ if( IsShowingPlaceholderText() || ( 0u == mLogicalModel->mText.Count() ) )
{
// Do not want to use the place-holder text to set the cursor position.
{
case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
{
- cursorPosition.x = 1.f;
+ cursorPosition.x = mEventData->mDecorator->GetCursorWidth();
break;
}
case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
}
// Set which cursors are active according the state.
- if( ( EventData::EDITING == mEventData->mState ) ||
- ( EventData::EDITING_WITH_POPUP == mEventData->mState ) ||
+ if( ( EventData::EDITING == mEventData->mState ) ||
+ ( EventData::EDITING_WITH_POPUP == mEventData->mState ) ||
+ ( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState ) ||
( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
{
if( cursorInfo.isSecondaryCursor )