{
#if defined(DEBUG_ENABLED)
- Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_CONTROLS");
+ Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
#endif
/**
float xBearing; ///< The x bearing of the first glyph.
};
-const std::string EMPTY_STRING("");
-
} // namespace
namespace Dali
* @param[in] numberOfGlyphs The number of glyphs.
* @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
* @param[in] visualModel The visual model.
- * @param[in] fontClient The font client.
+ * @param[in] metrics Used to access metrics from FontClient.
*/
void GetGlyphsMetrics( GlyphIndex glyphIndex,
Length numberOfGlyphs,
GlyphMetrics& glyphMetrics,
- VisualModelPtr visualModel,
- TextAbstraction::FontClient& fontClient )
+ VisualModelPtr& visualModel,
+ MetricsPtr& metrics )
{
const GlyphInfo* glyphsBuffer = visualModel->mGlyphs.Begin();
const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
Text::FontMetrics fontMetrics;
- fontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
+ metrics->GetFontMetrics( firstGlyph.fontId, fontMetrics );
glyphMetrics.fontHeight = fontMetrics.height;
glyphMetrics.advance = firstGlyph.advance;
void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
{
+ DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
+
// Calculate the operations to be done.
const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
if( GET_GLYPH_METRICS & operations )
{
GlyphInfo* glyphsBuffer = glyphs.Begin();
- mFontClient.GetGlyphMetrics( glyphsBuffer, numberOfGlyphs );
+ mMetrics->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 )
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 );
+ }
}
void Controller::Impl::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
{
if( mFontDefaults )
{
+ DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::GetDefaultFonts font family(%s)\n", mFontDefaults->mFontDescription.family.c_str() );
FontRun fontRun;
fontRun.characterRun.characterIndex = 0;
fontRun.characterRun.numberOfCharacters = numberOfCharacters;
FontId defaultFontId = 0u;
if( NULL == mFontDefaults )
{
- defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
- EMPTY_STRING );
+ TextAbstraction::FontDescription fontDescription;
+ defaultFontId = mFontClient.GetFontId( fontDescription );
}
else
{
}
Text::FontMetrics fontMetrics;
- mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
+ mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
return( fontMetrics.ascender - fontMetrics.descender );
}
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
{
return;
}
+ const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
+
//Get start and end position of selection
- uint32_t startOfSelectedText = mEventData->mLeftSelectionPosition;
- uint32_t lengthOfSelectedText = mEventData->mRightSelectionPosition - startOfSelectedText;
+ uint32_t startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
+ uint32_t lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
// Validate the start and end selection points
- if( ( startOfSelectedText >= 0 ) && ( ( startOfSelectedText + lengthOfSelectedText ) <= mLogicalModel->mText.Count() ) )
+ if( ( startOfSelectedText + lengthOfSelectedText ) <= mLogicalModel->mText.Count() )
{
//Get text as a UTF8 string
Vector<Character>& utf32Characters = mLogicalModel->mText;
Vector<Character>::Iterator last = first + lengthOfSelectedText;
currentText.Erase( first, last );
}
- mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition;
+ mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
mEventData->mScrollAfterDelete = true;
mEventData->mDecoratorUpdated = true;
}
const LineRun& firstLine = *lines.Begin();
const float height = firstLine.ascender + -firstLine.descender;
+ const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count();
+ const bool startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
+ const bool endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
+
// Swap the indices if the start is greater than the end.
- const bool indicesSwapped = ( selectionStart > selectionEnd );
+ const bool indicesSwapped = selectionStart > selectionEnd;
+
+ // Tell the decorator to flip the selection handles if needed.
+ mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
+
if( indicesSwapped )
{
std::swap( selectionStart, selectionEnd );
const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) );
- // Tell the decorator to swap the selection handles if needed.
- mEventData->mDecorator->SwapSelectionHandlesEnabled( firstLine.direction != indicesSwapped );
-
const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
// Traverse the glyphs.
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.
numberOfGlyphs,
glyphMetrics,
mVisualModel,
- mFontClient );
+ mMetrics );
const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
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;
}
primaryNumberOfGlyphs,
glyphMetrics,
mVisualModel,
- mFontClient );
+ mMetrics );
// Whether to add the glyph's advance to the cursor position.
// i.e if the paragraph is left to right and the logical cursor is zero, the position is the position of the first glyph and the advance is not added,
secondaryNumberOfGlyphs,
glyphMetrics,
mVisualModel,
- mFontClient );
+ mMetrics );
// Set the secondary cursor's position.
cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + ( isCurrentRightToLeft ? 0.f : glyphMetrics.advance );
FontId defaultFontId = 0u;
if( NULL == mFontDefaults )
{
- defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
- EMPTY_STRING );
+ TextAbstraction::FontDescription fontDescription;
+ defaultFontId = mFontClient.GetFontId( fontDescription );
}
else
{
}
Text::FontMetrics fontMetrics;
- mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
+ mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
lineHeight = fontMetrics.ascender - fontMetrics.descender;
}
// 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 )