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 - mAlignmentOffset.x;
261 const float yPosition = event.p3.mFloat - 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();
292 if( mEventData->mHorizontalScrollingEnabled )
294 const float displacementX = event.p2.mFloat;
295 mEventData->mScrollPosition.x += displacementX;
297 // Clamp between -space & 0 (and the text alignment).
298 if( actualSize.width > mControlSize.width )
300 const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
301 mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
302 mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
304 mEventData->mDecoratorUpdated = true;
308 mEventData->mScrollPosition.x = 0.f;
312 if( mEventData->mVerticalScrollingEnabled )
314 const float displacementY = event.p3.mFloat;
315 mEventData->mScrollPosition.y += displacementY;
317 // Clamp between -space & 0 (and the text alignment).
318 if( actualSize.height > mControlSize.height )
320 const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
321 mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
322 mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
324 mEventData->mDecoratorUpdated = true;
328 mEventData->mScrollPosition.y = 0.f;
334 void Controller::Impl::OnGrabHandleEvent( const Event& event )
336 if( NULL == mEventData )
338 // Nothing to do if there is no text input.
342 unsigned int state = event.p1.mUint;
344 if( GRAB_HANDLE_PRESSED == state )
346 float xPosition = event.p2.mFloat + mEventData->mScrollPosition.x;
347 float yPosition = event.p3.mFloat + mEventData->mScrollPosition.y;
349 mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
352 UpdateCursorPosition();
354 //mDecorator->HidePopup();
355 ChangeState ( EventData::EDITING );
357 else if( mEventData->mGrabHandlePopupEnabled &&
358 ( GRAB_HANDLE_RELEASED == state ) )
360 //mDecorator->ShowPopup();
361 ChangeState ( EventData::EDITING_WITH_POPUP );
362 mEventData->mDecoratorUpdated = true;
366 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
368 if( NULL == mEventData )
370 // Nothing to do if there is no text input.
374 // TODO - Find which word was selected
376 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
377 const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
379 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
380 const Vector<Vector2>::SizeType positionCount = positions.Count();
382 // Guard against glyphs which did not fit inside the layout
383 const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
387 float primaryX = positions[0].x;
388 float secondaryX = positions[count-1].x + glyphs[count-1].width;
390 // TODO - multi-line selection
391 const Vector<LineRun>& lines = mVisualModel->mLines;
392 float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
394 mEventData->mDecorator->SetPosition( PRIMARY_SELECTION_HANDLE, primaryX, 0.0f, height );
395 mEventData->mDecorator->SetPosition( SECONDARY_SELECTION_HANDLE, secondaryX, 0.0f, height );
397 mEventData->mDecorator->ClearHighlights();
398 mEventData->mDecorator->AddHighlight( primaryX, 0.0f, secondaryX, height );
402 void Controller::Impl::ChangeState( EventData::State newState )
404 if( NULL == mEventData )
406 // Nothing to do if there is no text input.
410 if( mEventData->mState != newState )
412 mEventData->mState = newState;
414 if( EventData::INACTIVE == mEventData->mState )
416 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
417 mEventData->mDecorator->StopCursorBlink();
418 mEventData->mDecorator->SetGrabHandleActive( false );
419 mEventData->mDecorator->SetSelectionActive( false );
420 mEventData->mDecorator->SetPopupActive( false );
421 mEventData->mDecoratorUpdated = true;
423 else if ( EventData::SELECTING == mEventData->mState )
425 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
426 mEventData->mDecorator->StopCursorBlink();
427 mEventData->mDecorator->SetGrabHandleActive( false );
428 mEventData->mDecorator->SetSelectionActive( true );
429 mEventData->mDecoratorUpdated = true;
431 else if( EventData::EDITING == mEventData->mState )
433 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
434 if( mEventData->mCursorBlinkEnabled )
436 mEventData->mDecorator->StartCursorBlink();
438 if( mEventData->mGrabHandleEnabled )
440 mEventData->mDecorator->SetGrabHandleActive( true );
442 if( mEventData->mGrabHandlePopupEnabled )
444 mEventData->mDecorator->SetPopupActive( false );
446 mEventData->mDecorator->SetSelectionActive( false );
447 mEventData->mDecoratorUpdated = true;
449 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
451 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
452 if( mEventData->mCursorBlinkEnabled )
454 mEventData->mDecorator->StartCursorBlink();
456 if( mEventData->mGrabHandleEnabled )
458 mEventData->mDecorator->SetGrabHandleActive( true );
460 if( mEventData->mGrabHandlePopupEnabled )
462 mEventData->mDecorator->SetPopupActive( true );
464 mEventData->mDecorator->SetSelectionActive( false );
465 mEventData->mDecoratorUpdated = true;
470 LineIndex Controller::Impl::GetClosestLine( float y ) const
472 float totalHeight = 0.f;
473 LineIndex lineIndex = 0u;
475 const Vector<LineRun>& lines = mVisualModel->mLines;
476 for( LineIndex endLine = lines.Count();
480 const LineRun& lineRun = lines[lineIndex];
481 totalHeight += lineRun.ascender + -lineRun.descender;
482 if( y < totalHeight )
491 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
494 if( NULL == mEventData )
496 // Nothing to do if there is no text input.
500 CharacterIndex logicalIndex = 0u;
502 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
503 const Length numberOfLines = mVisualModel->mLines.Count();
504 if( 0 == numberOfGlyphs ||
510 // Transform to visual model coords
511 visualX -= mEventData->mScrollPosition.x;
512 visualY -= mEventData->mScrollPosition.y;
514 // Find which line is closest
515 const LineIndex lineIndex = GetClosestLine( visualY );
516 const LineRun& line = mVisualModel->mLines[lineIndex];
518 // Get the positions of the glyphs.
519 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
520 const Vector2* const positionsBuffer = positions.Begin();
522 // Get the visual to logical conversion tables.
523 const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
524 const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
526 // Get the character to glyph conversion table.
527 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
529 // Get the glyphs per character table.
530 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
532 // If the vector is void, there is no right to left characters.
533 const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
535 const CharacterIndex startCharacter = line.characterRun.characterIndex;
536 const CharacterIndex endCharacter = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
537 DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
539 // Whether there is a hit on a glyph.
540 bool matched = false;
542 // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
543 CharacterIndex visualIndex = startCharacter;
544 for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
546 // The character in logical order.
547 const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
549 // The first glyph for that character in logical order.
550 const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
552 // The number of glyphs for that character
553 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
555 // Get the metrics for the group of glyphs.
556 GlyphMetrics glyphMetrics;
557 GetGlyphsMetrics( glyphLogicalOrderIndex,
563 const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
565 const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
567 if( visualX < glyphX )
574 // Return the logical position of the cursor in characters.
578 visualIndex = endCharacter;
581 return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
584 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
585 CursorInfo& cursorInfo )
587 // TODO: Check for multiline with \n, etc...
589 // Check if the logical position is the first or the last one of the text.
590 const bool isFirstPosition = 0u == logical;
591 const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
593 if( isFirstPosition && isLastPosition )
595 // There is zero characters. Get the default font.
597 FontId defaultFontId = 0u;
598 if( NULL == mFontDefaults )
600 defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
605 defaultFontId = mFontDefaults->GetFontId( mFontClient );
608 Text::FontMetrics fontMetrics;
609 mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
611 cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
612 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
614 cursorInfo.primaryPosition.x = 0.f;
615 cursorInfo.primaryPosition.y = 0.f;
617 // Nothing else to do.
621 // Get the previous logical index.
622 const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
624 // Decrease the logical index if it's the last one.
630 // Get the direction of the character and the previous one.
631 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
633 CharacterDirection isCurrentRightToLeft = false;
634 CharacterDirection isPreviousRightToLeft = false;
635 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
637 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
638 isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
641 // Get the line where the character is laid-out.
642 const LineRun* modelLines = mVisualModel->mLines.Begin();
644 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
645 const LineRun& line = *( modelLines + lineIndex );
647 // Get the paragraph's direction.
648 const CharacterDirection isRightToLeftParagraph = line.direction;
650 // Check whether there is an alternative position:
652 cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
653 ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
655 // Set the line height.
656 cursorInfo.lineHeight = line.ascender + -line.descender;
658 // Convert the cursor position into the glyph position.
659 CharacterIndex characterIndex = logical;
660 if( cursorInfo.isSecondaryCursor &&
661 ( isRightToLeftParagraph != isCurrentRightToLeft ) )
663 characterIndex = previousLogical;
666 const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
667 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
668 const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
670 // Get the metrics for the group of glyphs.
671 GlyphMetrics glyphMetrics;
672 GetGlyphsMetrics( currentGlyphIndex,
678 float interGlyphAdvance = 0.f;
679 if( !isLastPosition &&
680 ( numberOfCharacters > 1u ) )
682 const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
683 interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
686 // Get the glyph position and x bearing.
687 const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
689 // Set the cursor's height.
690 cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
693 cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
694 cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
698 // The position of the cursor after the last character needs special
699 // care depending on its direction and the direction of the paragraph.
701 if( cursorInfo.isSecondaryCursor )
703 // Need to find the first character after the last character with the paragraph's direction.
704 // i.e l0 l1 l2 r0 r1 should find r0.
706 // TODO: check for more than one line!
707 characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
708 characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
710 const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
711 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
713 const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
715 // Get the metrics for the group of glyphs.
716 GlyphMetrics glyphMetrics;
717 GetGlyphsMetrics( glyphIndex,
723 cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
725 cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
729 if( !isCurrentRightToLeft )
731 cursorInfo.primaryPosition.x += glyphMetrics.advance;
735 cursorInfo.primaryPosition.x -= glyphMetrics.advance;
740 // Set the alternative cursor position.
741 if( cursorInfo.isSecondaryCursor )
743 // Convert the cursor position into the glyph position.
744 const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
745 const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
746 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
748 // Get the glyph position.
749 const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
751 // Get the metrics for the group of glyphs.
752 GlyphMetrics glyphMetrics;
753 GetGlyphsMetrics( previousGlyphIndex,
759 // Set the cursor position and height.
760 cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
761 ( !isLastPosition && isCurrentRightToLeft ) ) ? glyphMetrics.advance : 0.f );
763 cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
765 cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
767 // Update the primary cursor height as well.
768 cursorInfo.primaryCursorHeight *= 0.5f;
772 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
774 if( NULL == mEventData )
776 // Nothing to do if there is no text input.
780 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
782 const Script script = mLogicalModel->GetScript( index );
783 const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
784 const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
786 Length numberOfCharacters = 0u;
787 if( TextAbstraction::LATIN == script )
789 // Prevents to jump the whole Latin ligatures like fi, ff, ...
790 numberOfCharacters = 1u;
794 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
795 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
797 while( 0u == numberOfCharacters )
799 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
804 if( index < mEventData->mPrimaryCursorPosition )
806 cursorIndex -= numberOfCharacters;
810 cursorIndex += numberOfCharacters;
816 void Controller::Impl::UpdateCursorPosition()
818 if( NULL == mEventData )
820 // Nothing to do if there is no text input.
824 CursorInfo cursorInfo;
825 GetCursorPosition( mEventData->mPrimaryCursorPosition,
828 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
829 cursorInfo.primaryPosition.x,
830 cursorInfo.primaryPosition.y,
831 cursorInfo.primaryCursorHeight,
832 cursorInfo.lineHeight );
834 if( cursorInfo.isSecondaryCursor )
836 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
837 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
838 cursorInfo.secondaryPosition.x,
839 cursorInfo.secondaryPosition.y,
840 cursorInfo.secondaryCursorHeight,
841 cursorInfo.lineHeight );
845 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
848 mEventData->mUpdateCursorPosition = false;
849 mEventData->mDecoratorUpdated = true;
852 void Controller::Impl::RequestRelayout()
854 mControlInterface.RequestTextRelayout();
859 } // namespace Toolkit