2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/public-api/adaptor-framework/key.h>
28 * @brief Some characters can be shaped in more than one glyph.
29 * This struct is used to retrieve metrics from these group of glyphs.
43 float fontHeight; ///< The font's height of that glyphs.
44 float advance; ///< The sum of all the advances of all the glyphs.
45 float ascender; ///< The font's ascender.
46 float xBearing; ///< The x bearing of the first glyph.
49 const std::string EMPTY_STRING("");
63 * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
65 * @param[in] glyphIndex The index to the first glyph.
66 * @param[in] numberOfGlyphs The number of glyphs.
67 * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
71 void GetGlyphsMetrics( GlyphIndex glyphIndex,
72 Length numberOfGlyphs,
73 GlyphMetrics& glyphMetrics,
74 VisualModelPtr visualModel,
75 TextAbstraction::FontClient& fontClient )
77 const GlyphInfo* glyphsBuffer = visualModel->mGlyphs.Begin();
79 const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
81 Text::FontMetrics fontMetrics;
82 fontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
84 glyphMetrics.fontHeight = fontMetrics.height;
85 glyphMetrics.advance = firstGlyph.advance;
86 glyphMetrics.ascender = fontMetrics.ascender;
87 glyphMetrics.xBearing = firstGlyph.xBearing;
89 for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
91 const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
93 glyphMetrics.advance += glyphInfo.advance;
97 EventData::EventData( DecoratorPtr decorator )
98 : mDecorator( decorator ),
103 mPrimaryCursorPosition( 0u ),
104 mSecondaryCursorPosition( 0u ),
105 mDecoratorUpdated( false ),
106 mCursorBlinkEnabled( true ),
107 mGrabHandleEnabled( true ),
108 mGrabHandlePopupEnabled( true ),
109 mSelectionEnabled( true ),
110 mHorizontalScrollingEnabled( true ),
111 mVerticalScrollingEnabled( false ),
112 mUpdateCursorPosition( false )
115 EventData::~EventData()
118 bool Controller::Impl::ProcessInputEvents()
120 if( NULL == mEventData )
122 // Nothing to do if there is no text input.
126 mEventData->mDecoratorUpdated = false;
128 if( mEventData->mDecorator )
130 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
131 iter != mEventData->mEventQueue.end();
136 case Event::KEYBOARD_FOCUS_GAIN_EVENT:
138 OnKeyboardFocus( true );
141 case Event::KEYBOARD_FOCUS_LOST_EVENT:
143 OnKeyboardFocus( false );
146 case Event::CURSOR_KEY_EVENT:
148 OnCursorKeyEvent( *iter );
151 case Event::TAP_EVENT:
156 case Event::PAN_EVENT:
161 case Event::GRAB_HANDLE_EVENT:
163 OnGrabHandleEvent( *iter );
170 // The cursor must also be repositioned after inserts into the model
171 if( mEventData->mUpdateCursorPosition )
173 UpdateCursorPosition();
174 mEventData->mUpdateCursorPosition = false;
177 mEventData->mEventQueue.clear();
179 return mEventData->mDecoratorUpdated;
182 void Controller::Impl::OnKeyboardFocus( bool hasFocus )
184 if( NULL == mEventData )
186 // Nothing to do if there is no text input.
192 ChangeState( EventData::INACTIVE );
196 ChangeState( EventData::EDITING );
200 void Controller::Impl::OnCursorKeyEvent( const Event& event )
202 if( NULL == mEventData )
204 // Nothing to do if there is no text input.
208 int keyCode = event.p1.mInt;
210 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
212 if( mEventData->mPrimaryCursorPosition > 0u )
214 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
217 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
219 if( mLogicalModel->GetNumberOfCharacters() > mEventData->mPrimaryCursorPosition )
221 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
224 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
228 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
233 UpdateCursorPosition();
236 void Controller::Impl::HandleCursorKey( int keyCode )
239 if( NULL == mEventData )
241 // Nothing to do if there is no text input.
246 void Controller::Impl::OnTapEvent( const Event& event )
248 if( NULL == mEventData )
250 // Nothing to do if there is no text input.
254 const unsigned int tapCount = event.p1.mUint;
258 ChangeState( EventData::EDITING );
260 const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
261 const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
263 mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
266 UpdateCursorPosition();
268 else if( mEventData->mSelectionEnabled &&
271 ChangeState( EventData::SELECTING );
273 RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
277 void Controller::Impl::OnPanEvent( const Event& event )
279 if( NULL == mEventData )
281 // Nothing to do if there is no text input.
285 int state = event.p1.mInt;
287 if( Gesture::Started == state ||
288 Gesture::Continuing == state )
290 const Vector2& actualSize = mVisualModel->GetActualSize();
291 const Vector2 currentScroll = mEventData->mScrollPosition;
293 if( mEventData->mHorizontalScrollingEnabled )
295 const float displacementX = event.p2.mFloat;
296 mEventData->mScrollPosition.x += displacementX;
298 // Clamp between -space & 0 (and the text alignment).
299 if( actualSize.width > mControlSize.width )
301 const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
302 mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
303 mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
305 mEventData->mDecoratorUpdated = true;
309 mEventData->mScrollPosition.x = 0.f;
313 if( mEventData->mVerticalScrollingEnabled )
315 const float displacementY = event.p3.mFloat;
316 mEventData->mScrollPosition.y += displacementY;
318 // Clamp between -space & 0 (and the text alignment).
319 if( actualSize.height > mControlSize.height )
321 const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
322 mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
323 mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
325 mEventData->mDecoratorUpdated = true;
329 mEventData->mScrollPosition.y = 0.f;
333 if( mEventData->mDecorator )
335 mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
340 void Controller::Impl::OnGrabHandleEvent( const Event& event )
342 if( NULL == mEventData )
344 // Nothing to do if there is no text input.
348 unsigned int state = event.p1.mUint;
350 if( GRAB_HANDLE_PRESSED == state )
352 // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
353 const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
354 const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
356 mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition, yPosition );
358 UpdateCursorPosition();
360 //mDecorator->HidePopup();
361 ChangeState ( EventData::EDITING );
363 else if( mEventData->mGrabHandlePopupEnabled &&
364 ( GRAB_HANDLE_RELEASED == state ) )
366 //mDecorator->ShowPopup();
367 ChangeState ( EventData::EDITING_WITH_POPUP );
368 mEventData->mDecoratorUpdated = true;
372 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
374 if( NULL == mEventData )
376 // Nothing to do if there is no text input.
380 // TODO - Find which word was selected
382 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
383 const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
385 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
386 const Vector<Vector2>::SizeType positionCount = positions.Count();
388 // Guard against glyphs which did not fit inside the layout
389 const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
393 float primaryX = positions[0].x + mEventData->mScrollPosition.x;
394 float secondaryX = positions[count-1].x + glyphs[count-1].width + mEventData->mScrollPosition.x;
396 // TODO - multi-line selection
397 const Vector<LineRun>& lines = mVisualModel->mLines;
398 float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
400 mEventData->mDecorator->SetPosition( PRIMARY_SELECTION_HANDLE, primaryX, mEventData->mScrollPosition.y, height );
401 mEventData->mDecorator->SetPosition( SECONDARY_SELECTION_HANDLE, secondaryX, mEventData->mScrollPosition.y, height );
403 mEventData->mDecorator->ClearHighlights();
404 mEventData->mDecorator->AddHighlight( primaryX, mEventData->mScrollPosition.y, secondaryX, height + mEventData->mScrollPosition.y );
408 void Controller::Impl::ChangeState( EventData::State newState )
410 if( NULL == mEventData )
412 // Nothing to do if there is no text input.
416 if( mEventData->mState != newState )
418 mEventData->mState = newState;
420 if( EventData::INACTIVE == mEventData->mState )
422 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
423 mEventData->mDecorator->StopCursorBlink();
424 mEventData->mDecorator->SetGrabHandleActive( false );
425 mEventData->mDecorator->SetSelectionActive( false );
426 mEventData->mDecorator->SetPopupActive( false );
427 mEventData->mDecoratorUpdated = true;
429 else if ( EventData::SELECTING == mEventData->mState )
431 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
432 mEventData->mDecorator->StopCursorBlink();
433 mEventData->mDecorator->SetGrabHandleActive( false );
434 mEventData->mDecorator->SetSelectionActive( true );
435 mEventData->mDecoratorUpdated = true;
437 else if( EventData::EDITING == mEventData->mState )
439 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
440 if( mEventData->mCursorBlinkEnabled )
442 mEventData->mDecorator->StartCursorBlink();
444 if( mEventData->mGrabHandleEnabled )
446 mEventData->mDecorator->SetGrabHandleActive( true );
448 if( mEventData->mGrabHandlePopupEnabled )
450 mEventData->mDecorator->SetPopupActive( false );
452 mEventData->mDecorator->SetSelectionActive( false );
453 mEventData->mDecoratorUpdated = true;
455 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
457 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
458 if( mEventData->mCursorBlinkEnabled )
460 mEventData->mDecorator->StartCursorBlink();
462 if( mEventData->mGrabHandleEnabled )
464 mEventData->mDecorator->SetGrabHandleActive( true );
466 if( mEventData->mGrabHandlePopupEnabled )
468 mEventData->mDecorator->SetPopupActive( true );
470 mEventData->mDecorator->SetSelectionActive( false );
471 mEventData->mDecoratorUpdated = true;
476 LineIndex Controller::Impl::GetClosestLine( float y ) const
478 float totalHeight = 0.f;
479 LineIndex lineIndex = 0u;
481 const Vector<LineRun>& lines = mVisualModel->mLines;
482 for( LineIndex endLine = lines.Count();
486 const LineRun& lineRun = lines[lineIndex];
487 totalHeight += lineRun.ascender + -lineRun.descender;
488 if( y < totalHeight )
497 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
500 if( NULL == mEventData )
502 // Nothing to do if there is no text input.
506 CharacterIndex logicalIndex = 0u;
508 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
509 const Length numberOfLines = mVisualModel->mLines.Count();
510 if( 0 == numberOfGlyphs ||
516 // Find which line is closest
517 const LineIndex lineIndex = GetClosestLine( visualY );
518 const LineRun& line = mVisualModel->mLines[lineIndex];
520 // Get the positions of the glyphs.
521 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
522 const Vector2* const positionsBuffer = positions.Begin();
524 // Get the visual to logical conversion tables.
525 const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
526 const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
528 // Get the character to glyph conversion table.
529 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
531 // Get the glyphs per character table.
532 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
534 // If the vector is void, there is no right to left characters.
535 const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
537 const CharacterIndex startCharacter = line.characterRun.characterIndex;
538 const CharacterIndex endCharacter = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
539 DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
541 // Whether there is a hit on a glyph.
542 bool matched = false;
544 // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
545 CharacterIndex visualIndex = startCharacter;
546 for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
548 // The character in logical order.
549 const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
551 // The first glyph for that character in logical order.
552 const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
554 // The number of glyphs for that character
555 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
557 // Get the metrics for the group of glyphs.
558 GlyphMetrics glyphMetrics;
559 GetGlyphsMetrics( glyphLogicalOrderIndex,
565 const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
567 const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
569 if( visualX < glyphX )
576 // Return the logical position of the cursor in characters.
580 visualIndex = endCharacter;
583 return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
586 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
587 CursorInfo& cursorInfo )
589 // TODO: Check for multiline with \n, etc...
591 // Check if the logical position is the first or the last one of the text.
592 const bool isFirstPosition = 0u == logical;
593 const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
595 if( isFirstPosition && isLastPosition )
597 // There is zero characters. Get the default font.
599 FontId defaultFontId = 0u;
600 if( NULL == mFontDefaults )
602 defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
607 defaultFontId = mFontDefaults->GetFontId( mFontClient );
610 Text::FontMetrics fontMetrics;
611 mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
613 cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
614 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
616 cursorInfo.primaryPosition.x = 0.f;
617 cursorInfo.primaryPosition.y = 0.f;
619 // Nothing else to do.
623 // Get the previous logical index.
624 const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
626 // Decrease the logical index if it's the last one.
632 // Get the direction of the character and the previous one.
633 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
635 CharacterDirection isCurrentRightToLeft = false;
636 CharacterDirection isPreviousRightToLeft = false;
637 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
639 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
640 isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
643 // Get the line where the character is laid-out.
644 const LineRun* modelLines = mVisualModel->mLines.Begin();
646 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
647 const LineRun& line = *( modelLines + lineIndex );
649 // Get the paragraph's direction.
650 const CharacterDirection isRightToLeftParagraph = line.direction;
652 // Check whether there is an alternative position:
654 cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
655 ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
657 // Set the line height.
658 cursorInfo.lineHeight = line.ascender + -line.descender;
660 // Convert the cursor position into the glyph position.
661 CharacterIndex characterIndex = logical;
662 if( cursorInfo.isSecondaryCursor &&
663 ( isRightToLeftParagraph != isCurrentRightToLeft ) )
665 characterIndex = previousLogical;
668 const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
669 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
670 const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
672 // Get the metrics for the group of glyphs.
673 GlyphMetrics glyphMetrics;
674 GetGlyphsMetrics( currentGlyphIndex,
680 float interGlyphAdvance = 0.f;
681 if( !isLastPosition &&
682 ( numberOfCharacters > 1u ) )
684 const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
685 interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
688 // Get the glyph position and x bearing.
689 const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
691 // Set the cursor's height.
692 cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
695 cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
696 cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
700 // The position of the cursor after the last character needs special
701 // care depending on its direction and the direction of the paragraph.
703 if( cursorInfo.isSecondaryCursor )
705 // Need to find the first character after the last character with the paragraph's direction.
706 // i.e l0 l1 l2 r0 r1 should find r0.
708 // TODO: check for more than one line!
709 characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
710 characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
712 const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
713 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
715 const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
717 // Get the metrics for the group of glyphs.
718 GlyphMetrics glyphMetrics;
719 GetGlyphsMetrics( glyphIndex,
725 cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
727 cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
731 if( !isCurrentRightToLeft )
733 cursorInfo.primaryPosition.x += glyphMetrics.advance;
737 cursorInfo.primaryPosition.x -= glyphMetrics.advance;
742 // Set the alternative cursor position.
743 if( cursorInfo.isSecondaryCursor )
745 // Convert the cursor position into the glyph position.
746 const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
747 const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
748 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
750 // Get the glyph position.
751 const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
753 // Get the metrics for the group of glyphs.
754 GlyphMetrics glyphMetrics;
755 GetGlyphsMetrics( previousGlyphIndex,
761 // Set the cursor position and height.
762 cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
763 ( !isLastPosition && isCurrentRightToLeft ) ) ? glyphMetrics.advance : 0.f );
765 cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
767 cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
769 // Update the primary cursor height as well.
770 cursorInfo.primaryCursorHeight *= 0.5f;
774 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
776 if( NULL == mEventData )
778 // Nothing to do if there is no text input.
782 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
784 const Script script = mLogicalModel->GetScript( index );
785 const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
786 const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
788 Length numberOfCharacters = 0u;
789 if( TextAbstraction::LATIN == script )
791 // Prevents to jump the whole Latin ligatures like fi, ff, ...
792 numberOfCharacters = 1u;
796 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
797 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
799 while( 0u == numberOfCharacters )
801 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
806 if( index < mEventData->mPrimaryCursorPosition )
808 cursorIndex -= numberOfCharacters;
812 cursorIndex += numberOfCharacters;
818 void Controller::Impl::UpdateCursorPosition()
820 if( NULL == mEventData )
822 // Nothing to do if there is no text input.
826 CursorInfo cursorInfo;
827 GetCursorPosition( mEventData->mPrimaryCursorPosition,
830 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
831 cursorInfo.primaryPosition.x + mEventData->mScrollPosition.x + mAlignmentOffset.x,
832 cursorInfo.primaryPosition.y + mEventData->mScrollPosition.y + mAlignmentOffset.y,
833 cursorInfo.primaryCursorHeight,
834 cursorInfo.lineHeight );
836 if( cursorInfo.isSecondaryCursor )
838 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
839 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
840 cursorInfo.secondaryPosition.x + mEventData->mScrollPosition.x + mAlignmentOffset.x,
841 cursorInfo.secondaryPosition.y + mEventData->mScrollPosition.y + mAlignmentOffset.y,
842 cursorInfo.secondaryCursorHeight,
843 cursorInfo.lineHeight );
847 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
850 mEventData->mUpdateCursorPosition = false;
851 mEventData->mDecoratorUpdated = true;
854 void Controller::Impl::RequestRelayout()
856 mControlInterface.RequestTextRelayout();
861 } // namespace Toolkit