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 ),
113 mScrollAfterUpdateCursorPosition( false )
116 EventData::~EventData()
119 bool Controller::Impl::ProcessInputEvents()
121 if( NULL == mEventData )
123 // Nothing to do if there is no text input.
127 mEventData->mDecoratorUpdated = false;
129 if( mEventData->mDecorator )
131 for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
132 iter != mEventData->mEventQueue.end();
137 case Event::KEYBOARD_FOCUS_GAIN_EVENT:
139 OnKeyboardFocus( true );
142 case Event::KEYBOARD_FOCUS_LOST_EVENT:
144 OnKeyboardFocus( false );
147 case Event::CURSOR_KEY_EVENT:
149 OnCursorKeyEvent( *iter );
152 case Event::TAP_EVENT:
157 case Event::PAN_EVENT:
162 case Event::GRAB_HANDLE_EVENT:
164 OnGrabHandleEvent( *iter );
171 // The cursor must also be repositioned after inserts into the model
172 if( mEventData->mUpdateCursorPosition )
174 // Updates the cursor position and scrolls the text to make it visible.
176 UpdateCursorPosition();
178 if( mEventData->mScrollAfterUpdateCursorPosition )
180 ScrollToMakeCursorVisible();
181 mEventData->mScrollAfterUpdateCursorPosition = false;
184 mEventData->mUpdateCursorPosition = false;
187 mEventData->mEventQueue.clear();
189 return mEventData->mDecoratorUpdated;
192 void Controller::Impl::OnKeyboardFocus( bool hasFocus )
194 if( NULL == mEventData )
196 // Nothing to do if there is no text input.
202 ChangeState( EventData::INACTIVE );
206 ChangeState( EventData::EDITING );
210 void Controller::Impl::OnCursorKeyEvent( const Event& event )
212 if( NULL == mEventData )
214 // Nothing to do if there is no text input.
218 int keyCode = event.p1.mInt;
220 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
222 if( mEventData->mPrimaryCursorPosition > 0u )
224 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
227 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
229 if( mLogicalModel->GetNumberOfCharacters() > mEventData->mPrimaryCursorPosition )
231 mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
234 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
238 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
243 mEventData->mUpdateCursorPosition = true;
244 mEventData->mScrollAfterUpdateCursorPosition = true;
247 void Controller::Impl::HandleCursorKey( int keyCode )
250 if( NULL == mEventData )
252 // Nothing to do if there is no text input.
257 void Controller::Impl::OnTapEvent( const Event& event )
259 if( NULL == mEventData )
261 // Nothing to do if there is no text input.
265 const unsigned int tapCount = event.p1.mUint;
269 ChangeState( EventData::EDITING );
271 const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
272 const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
274 mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
277 mEventData->mUpdateCursorPosition = true;
278 mEventData->mScrollAfterUpdateCursorPosition = true;
280 else if( mEventData->mSelectionEnabled &&
283 ChangeState( EventData::SELECTING );
285 RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
289 void Controller::Impl::OnPanEvent( const Event& event )
291 if( NULL == mEventData )
293 // Nothing to do if there is no text input.
297 int state = event.p1.mInt;
299 if( Gesture::Started == state ||
300 Gesture::Continuing == state )
302 const Vector2& actualSize = mVisualModel->GetActualSize();
303 const Vector2 currentScroll = mEventData->mScrollPosition;
305 if( mEventData->mHorizontalScrollingEnabled )
307 const float displacementX = event.p2.mFloat;
308 mEventData->mScrollPosition.x += displacementX;
310 ClampHorizontalScroll( actualSize );
313 if( mEventData->mVerticalScrollingEnabled )
315 const float displacementY = event.p3.mFloat;
316 mEventData->mScrollPosition.y += displacementY;
318 ClampVerticalScroll( actualSize );
321 if( mEventData->mDecorator )
323 mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
328 void Controller::Impl::OnGrabHandleEvent( const Event& event )
330 if( NULL == mEventData )
332 // Nothing to do if there is no text input.
336 unsigned int state = event.p1.mUint;
338 if( GRAB_HANDLE_PRESSED == state )
340 // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
341 const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
342 const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
344 //mDecorator->HidePopup();
345 ChangeState ( EventData::EDITING );
347 const CharacterIndex newCursorPosition = GetClosestCursorIndex( xPosition, yPosition );
349 if( newCursorPosition != mEventData->mPrimaryCursorPosition )
351 mEventData->mPrimaryCursorPosition = newCursorPosition;
352 mEventData->mUpdateCursorPosition = true;
355 else if( mEventData->mGrabHandlePopupEnabled &&
356 ( GRAB_HANDLE_RELEASED == state ) )
358 //mDecorator->ShowPopup();
359 ChangeState ( EventData::EDITING_WITH_POPUP );
360 mEventData->mUpdateCursorPosition = true;
361 mEventData->mDecoratorUpdated = true;
365 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
367 if( NULL == mEventData )
369 // Nothing to do if there is no text input.
373 // TODO - Find which word was selected
375 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
376 const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
378 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
379 const Vector<Vector2>::SizeType positionCount = positions.Count();
381 // Guard against glyphs which did not fit inside the layout
382 const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
386 float primaryX = positions[0].x + mEventData->mScrollPosition.x;
387 float secondaryX = positions[count-1].x + glyphs[count-1].width + mEventData->mScrollPosition.x;
389 // TODO - multi-line selection
390 const Vector<LineRun>& lines = mVisualModel->mLines;
391 float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
393 mEventData->mDecorator->SetPosition( PRIMARY_SELECTION_HANDLE, primaryX, mEventData->mScrollPosition.y, height );
394 mEventData->mDecorator->SetPosition( SECONDARY_SELECTION_HANDLE, secondaryX, mEventData->mScrollPosition.y, height );
396 mEventData->mDecorator->ClearHighlights();
397 mEventData->mDecorator->AddHighlight( primaryX, mEventData->mScrollPosition.y, secondaryX, height + mEventData->mScrollPosition.y );
401 void Controller::Impl::ChangeState( EventData::State newState )
403 if( NULL == mEventData )
405 // Nothing to do if there is no text input.
409 if( mEventData->mState != newState )
411 mEventData->mState = newState;
413 if( EventData::INACTIVE == mEventData->mState )
415 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
416 mEventData->mDecorator->StopCursorBlink();
417 mEventData->mDecorator->SetGrabHandleActive( false );
418 mEventData->mDecorator->SetSelectionActive( false );
419 mEventData->mDecorator->SetPopupActive( false );
420 mEventData->mDecoratorUpdated = true;
422 else if ( EventData::SELECTING == mEventData->mState )
424 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
425 mEventData->mDecorator->StopCursorBlink();
426 mEventData->mDecorator->SetGrabHandleActive( false );
427 mEventData->mDecorator->SetSelectionActive( true );
428 mEventData->mDecoratorUpdated = true;
430 else if( EventData::EDITING == mEventData->mState )
432 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
433 if( mEventData->mCursorBlinkEnabled )
435 mEventData->mDecorator->StartCursorBlink();
437 if( mEventData->mGrabHandleEnabled )
439 mEventData->mDecorator->SetGrabHandleActive( true );
441 if( mEventData->mGrabHandlePopupEnabled )
443 mEventData->mDecorator->SetPopupActive( false );
445 mEventData->mDecorator->SetSelectionActive( false );
446 mEventData->mDecoratorUpdated = true;
448 else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
450 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
451 if( mEventData->mCursorBlinkEnabled )
453 mEventData->mDecorator->StartCursorBlink();
455 if( mEventData->mGrabHandleEnabled )
457 mEventData->mDecorator->SetGrabHandleActive( true );
459 if( mEventData->mGrabHandlePopupEnabled )
461 mEventData->mDecorator->SetPopupActive( true );
463 mEventData->mDecorator->SetSelectionActive( false );
464 mEventData->mDecoratorUpdated = true;
469 LineIndex Controller::Impl::GetClosestLine( float y ) const
471 float totalHeight = 0.f;
472 LineIndex lineIndex = 0u;
474 const Vector<LineRun>& lines = mVisualModel->mLines;
475 for( LineIndex endLine = lines.Count();
479 const LineRun& lineRun = lines[lineIndex];
480 totalHeight += lineRun.ascender + -lineRun.descender;
481 if( y < totalHeight )
490 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
493 if( NULL == mEventData )
495 // Nothing to do if there is no text input.
499 CharacterIndex logicalIndex = 0u;
501 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
502 const Length numberOfLines = mVisualModel->mLines.Count();
503 if( 0 == numberOfGlyphs ||
509 // Find which line is closest
510 const LineIndex lineIndex = GetClosestLine( visualY );
511 const LineRun& line = mVisualModel->mLines[lineIndex];
513 // Get the positions of the glyphs.
514 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
515 const Vector2* const positionsBuffer = positions.Begin();
517 // Get the visual to logical conversion tables.
518 const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
519 const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
521 // Get the character to glyph conversion table.
522 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
524 // Get the glyphs per character table.
525 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
527 // If the vector is void, there is no right to left characters.
528 const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
530 const CharacterIndex startCharacter = line.characterRun.characterIndex;
531 const CharacterIndex endCharacter = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
532 DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
534 // Whether there is a hit on a glyph.
535 bool matched = false;
537 // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
538 CharacterIndex visualIndex = startCharacter;
539 for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
541 // The character in logical order.
542 const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
544 // The first glyph for that character in logical order.
545 const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
547 // The number of glyphs for that character
548 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
550 // Get the metrics for the group of glyphs.
551 GlyphMetrics glyphMetrics;
552 GetGlyphsMetrics( glyphLogicalOrderIndex,
558 const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
560 const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
562 if( visualX < glyphX )
569 // Return the logical position of the cursor in characters.
573 visualIndex = endCharacter;
576 return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
579 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
580 CursorInfo& cursorInfo )
582 // TODO: Check for multiline with \n, etc...
584 // Check if the logical position is the first or the last one of the text.
585 const bool isFirstPosition = 0u == logical;
586 const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
588 if( isFirstPosition && isLastPosition )
590 // There is zero characters. Get the default font.
592 FontId defaultFontId = 0u;
593 if( NULL == mFontDefaults )
595 defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
600 defaultFontId = mFontDefaults->GetFontId( mFontClient );
603 Text::FontMetrics fontMetrics;
604 mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
606 cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
607 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
609 cursorInfo.primaryPosition.x = 0.f;
610 cursorInfo.primaryPosition.y = 0.f;
612 // Nothing else to do.
616 // Get the previous logical index.
617 const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
619 // Decrease the logical index if it's the last one.
625 // Get the direction of the character and the previous one.
626 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
628 CharacterDirection isCurrentRightToLeft = false;
629 CharacterDirection isPreviousRightToLeft = false;
630 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
632 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
633 isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
636 // Get the line where the character is laid-out.
637 const LineRun* modelLines = mVisualModel->mLines.Begin();
639 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
640 const LineRun& line = *( modelLines + lineIndex );
642 // Get the paragraph's direction.
643 const CharacterDirection isRightToLeftParagraph = line.direction;
645 // Check whether there is an alternative position:
647 cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
648 ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
650 // Set the line height.
651 cursorInfo.lineHeight = line.ascender + -line.descender;
653 // Convert the cursor position into the glyph position.
654 CharacterIndex characterIndex = logical;
655 if( cursorInfo.isSecondaryCursor &&
656 ( isRightToLeftParagraph != isCurrentRightToLeft ) )
658 characterIndex = previousLogical;
661 const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
662 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
663 const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
665 // Get the metrics for the group of glyphs.
666 GlyphMetrics glyphMetrics;
667 GetGlyphsMetrics( currentGlyphIndex,
673 float interGlyphAdvance = 0.f;
674 if( !isLastPosition &&
675 ( numberOfCharacters > 1u ) )
677 const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
678 interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
681 // Get the glyph position and x bearing.
682 const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
684 // Set the cursor's height.
685 cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
688 cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
689 cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
693 // The position of the cursor after the last character needs special
694 // care depending on its direction and the direction of the paragraph.
696 if( cursorInfo.isSecondaryCursor )
698 // Need to find the first character after the last character with the paragraph's direction.
699 // i.e l0 l1 l2 r0 r1 should find r0.
701 // TODO: check for more than one line!
702 characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
703 characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
705 const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
706 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
708 const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
710 // Get the metrics for the group of glyphs.
711 GlyphMetrics glyphMetrics;
712 GetGlyphsMetrics( glyphIndex,
718 cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
720 cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
724 if( !isCurrentRightToLeft )
726 cursorInfo.primaryPosition.x += glyphMetrics.advance;
730 cursorInfo.primaryPosition.x -= glyphMetrics.advance;
735 // Set the alternative cursor position.
736 if( cursorInfo.isSecondaryCursor )
738 // Convert the cursor position into the glyph position.
739 const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
740 const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
741 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
743 // Get the glyph position.
744 const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
746 // Get the metrics for the group of glyphs.
747 GlyphMetrics glyphMetrics;
748 GetGlyphsMetrics( previousGlyphIndex,
754 // Set the cursor position and height.
755 cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
756 ( !isLastPosition && isCurrentRightToLeft ) ) ? glyphMetrics.advance : 0.f );
758 cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
760 cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
762 // Update the primary cursor height as well.
763 cursorInfo.primaryCursorHeight *= 0.5f;
767 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
769 if( NULL == mEventData )
771 // Nothing to do if there is no text input.
775 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
777 const Script script = mLogicalModel->GetScript( index );
778 const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
779 const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
781 Length numberOfCharacters = 0u;
782 if( TextAbstraction::LATIN == script )
784 // Prevents to jump the whole Latin ligatures like fi, ff, ...
785 numberOfCharacters = 1u;
789 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
790 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
792 while( 0u == numberOfCharacters )
794 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
799 if( index < mEventData->mPrimaryCursorPosition )
801 cursorIndex -= numberOfCharacters;
805 cursorIndex += numberOfCharacters;
811 void Controller::Impl::UpdateCursorPosition()
813 if( NULL == mEventData )
815 // Nothing to do if there is no text input.
819 CursorInfo cursorInfo;
820 GetCursorPosition( mEventData->mPrimaryCursorPosition,
823 mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
824 cursorInfo.primaryPosition.x + mEventData->mScrollPosition.x + mAlignmentOffset.x,
825 cursorInfo.primaryPosition.y + mEventData->mScrollPosition.y + mAlignmentOffset.y,
826 cursorInfo.primaryCursorHeight,
827 cursorInfo.lineHeight );
829 if( cursorInfo.isSecondaryCursor )
831 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
832 mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
833 cursorInfo.secondaryPosition.x + mEventData->mScrollPosition.x + mAlignmentOffset.x,
834 cursorInfo.secondaryPosition.y + mEventData->mScrollPosition.y + mAlignmentOffset.y,
835 cursorInfo.secondaryCursorHeight,
836 cursorInfo.lineHeight );
840 mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
843 mEventData->mUpdateCursorPosition = false;
844 mEventData->mDecoratorUpdated = true;
847 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
849 // Clamp between -space & 0 (and the text alignment).
850 if( actualSize.width > mControlSize.width )
852 const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
853 mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
854 mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
856 mEventData->mDecoratorUpdated = true;
860 mEventData->mScrollPosition.x = 0.f;
864 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
866 // Clamp between -space & 0 (and the text alignment).
867 if( actualSize.height > mControlSize.height )
869 const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
870 mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
871 mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
873 mEventData->mDecoratorUpdated = true;
877 mEventData->mScrollPosition.y = 0.f;
881 void Controller::Impl::ScrollToMakeCursorVisible()
883 if( NULL == mEventData )
885 // Nothing to do if there is no text input.
889 const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
892 bool updateDecorator = false;
893 if( primaryCursorPosition.x < 0.f )
895 offset.x = -primaryCursorPosition.x;
896 mEventData->mScrollPosition.x += offset.x;
897 updateDecorator = true;
899 else if( primaryCursorPosition.x > mControlSize.width )
901 offset.x = mControlSize.width - primaryCursorPosition.x;
902 mEventData->mScrollPosition.x += offset.x;
903 updateDecorator = true;
906 if( updateDecorator && mEventData->mDecorator )
908 mEventData->mDecorator->UpdatePositions( offset );
911 // TODO : calculate the Y scroll.
914 void Controller::Impl::RequestRelayout()
916 mControlInterface.RequestTextRelayout();
921 } // namespace Toolkit