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.h>
24 #include <dali/public-api/adaptor-framework/key.h>
25 #include <dali/public-api/text-abstraction/font-client.h>
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/character-set-conversion.h>
30 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
31 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
32 #include <dali-toolkit/internal/text/logical-model-impl.h>
33 #include <dali-toolkit/internal/text/multi-language-support.h>
34 #include <dali-toolkit/internal/text/script-run.h>
35 #include <dali-toolkit/internal/text/segmentation.h>
36 #include <dali-toolkit/internal/text/shaper.h>
37 #include <dali-toolkit/internal/text/text-io.h>
38 #include <dali-toolkit/internal/text/text-view.h>
39 #include <dali-toolkit/internal/text/visual-model-impl.h>
46 const float MAX_FLOAT = std::numeric_limits<float>::max();
50 REPLACE_TEXT, ///< Replace the entire text
51 INSERT_TEXT, ///< Insert characters at the current cursor position
52 DELETE_TEXT ///< Delete a character at the current cursor position
61 const std::string EMPTY_STRING("");
74 struct Controller::FontDefaults
77 : mDefaultPointSize(0.0f),
82 FontId GetFontId( TextAbstraction::FontClient& fontClient )
86 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
87 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
93 std::string mDefaultFontFamily;
94 std::string mDefaultFontStyle;
95 float mDefaultPointSize;
99 struct Controller::TextInput
101 // Used to queue input events until DoRelayout()
104 KEYBOARD_FOCUS_GAIN_EVENT,
105 KEYBOARD_FOCUS_LOST_EVENT,
121 Event( EventType eventType )
140 primaryCursorHeight( 0.f ),
141 secondaryCursorHeight( 0.f ),
142 isSecondaryCursor( false )
148 Vector2 primaryPosition; ///< The primary cursor's position.
149 Vector2 secondaryPosition; ///< The secondary cursor's position.
150 float lineHeight; ///< The height of the line where the cursor is placed.
151 float primaryCursorHeight; ///< The primary cursor's height.
152 float secondaryCursorHeight; ///< The secondary cursor's height.
153 bool isSecondaryCursor; ///< Whether the secondary cursor is valid.
157 * @brief Some characters can be shaped in more than one glyph.
158 * This struct is used to retrieve metrics from these group of glyphs.
172 float fontHeight; ///< The font's height of that glyphs.
173 float advance; ///< The sum of all the advances of all the glyphs.
174 float ascender; ///< The font's ascender.
175 float xBearing; ///< The x bearing of the first glyph.
186 TextInput( LogicalModelPtr logicalModel,
187 VisualModelPtr visualModel,
188 DecoratorPtr decorator,
189 FontDefaults* fontDefaults,
190 TextAbstraction::FontClient& fontClient )
191 : mLogicalModel( logicalModel ),
192 mVisualModel( visualModel ),
193 mDecorator( decorator ),
194 mFontDefaults( fontDefaults ),
195 mFontClient( fontClient ),
197 mPrimaryCursorPosition( 0u ),
198 mSecondaryCursorPosition( 0u ),
199 mDecoratorUpdated( false ),
200 mCursorBlinkEnabled( true ),
201 mGrabHandleEnabled( true ),
202 mGrabHandlePopupEnabled( true ),
203 mSelectionEnabled( true ),
204 mHorizontalScrollingEnabled( true ),
205 mVerticalScrollingEnabled( false ),
206 mUpdateCursorPosition( false )
210 * @brief Helper to move the cursor, grab handle etc.
212 bool ProcessInputEvents( const Vector2& controlSize,
213 const Vector2& alignmentOffset )
215 mDecoratorUpdated = false;
219 for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
223 case KEYBOARD_FOCUS_GAIN_EVENT:
225 OnKeyboardFocus( true );
228 case KEYBOARD_FOCUS_LOST_EVENT:
230 OnKeyboardFocus( false );
233 case CURSOR_KEY_EVENT:
235 OnCursorKeyEvent( *iter );
240 OnTapEvent( *iter, alignmentOffset );
245 OnPanEvent( *iter, controlSize, alignmentOffset );
248 case GRAB_HANDLE_EVENT:
250 OnGrabHandleEvent( *iter );
257 // The cursor must also be repositioned after inserts into the model
258 if( mUpdateCursorPosition )
260 UpdateCursorPosition();
261 mUpdateCursorPosition = false;
266 return mDecoratorUpdated;
269 void OnKeyboardFocus( bool hasFocus )
273 ChangeState( INACTIVE );
277 ChangeState( EDITING );
281 void OnCursorKeyEvent( const Event& event )
283 int keyCode = event.p1.mInt;
285 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
287 if( mPrimaryCursorPosition > 0u )
289 mPrimaryCursorPosition = CalculateNewCursorIndex( mPrimaryCursorPosition - 1u );
292 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
294 if( mLogicalModel->GetNumberOfCharacters() > mPrimaryCursorPosition )
296 mPrimaryCursorPosition = CalculateNewCursorIndex( mPrimaryCursorPosition );
299 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
303 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
308 UpdateCursorPosition();
311 void HandleCursorKey( int keyCode )
316 void OnTapEvent( const Event& event,
317 const Vector2& alignmentOffset )
319 unsigned int tapCount = event.p1.mUint;
323 ChangeState( EDITING );
325 float xPosition = event.p2.mFloat - alignmentOffset.x;
326 float yPosition = event.p3.mFloat - alignmentOffset.y;
328 mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
331 UpdateCursorPosition();
333 else if( mSelectionEnabled &&
336 ChangeState( SELECTING );
338 RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
342 void OnPanEvent( const Event& event,
343 const Vector2& controlSize,
344 const Vector2& alignmentOffset )
346 int state = event.p1.mInt;
348 if( Gesture::Started == state ||
349 Gesture::Continuing == state )
351 const Vector2& actualSize = mVisualModel->GetActualSize();
353 if( mHorizontalScrollingEnabled )
355 const float displacementX = event.p2.mFloat;
356 mScrollPosition.x += displacementX;
358 // Clamp between -space & 0 (and the text alignment).
359 const float contentWidth = actualSize.width;
360 if( contentWidth > controlSize.width )
362 const float space = ( contentWidth - controlSize.width ) + alignmentOffset.x;
363 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
364 mScrollPosition.x = ( mScrollPosition.x > -alignmentOffset.x ) ? -alignmentOffset.x : mScrollPosition.x;
366 mDecoratorUpdated = true;
370 mScrollPosition.x = 0.f;
374 if( mVerticalScrollingEnabled )
376 const float displacementY = event.p3.mFloat;
377 mScrollPosition.y += displacementY;
379 // Clamp between -space & 0 (and the text alignment).
380 if( actualSize.height > controlSize.height )
382 const float space = ( actualSize.height - controlSize.height ) + alignmentOffset.y;
383 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
384 mScrollPosition.y = ( mScrollPosition.y > -alignmentOffset.y ) ? -alignmentOffset.y : mScrollPosition.y;
386 mDecoratorUpdated = true;
390 mScrollPosition.y = 0.f;
396 void OnGrabHandleEvent( const Event& event )
398 unsigned int state = event.p1.mUint;
400 if( GRAB_HANDLE_PRESSED == state )
402 float xPosition = event.p2.mFloat + mScrollPosition.x;
403 float yPosition = event.p3.mFloat + mScrollPosition.y;
405 mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
408 UpdateCursorPosition();
410 //mDecorator->HidePopup();
411 ChangeState ( EDITING );
413 else if ( mGrabHandlePopupEnabled &&
414 GRAB_HANDLE_RELEASED == state )
416 //mDecorator->ShowPopup();
417 ChangeState ( EDITING_WITH_POPUP );
418 mDecoratorUpdated = true;
422 void RepositionSelectionHandles( float visualX, float visualY )
424 // TODO - Find which word was selected
426 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
427 const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
429 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
430 const Vector<Vector2>::SizeType positionCount = positions.Count();
432 // Guard against glyphs which did not fit inside the layout
433 const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
437 float primaryX = positions[0].x;
438 float secondaryX = positions[count-1].x + glyphs[count-1].width;
440 // TODO - multi-line selection
441 const Vector<LineRun>& lines = mVisualModel->mLines;
442 float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
444 mDecorator->SetPosition( PRIMARY_SELECTION_HANDLE, primaryX, 0.0f, height );
445 mDecorator->SetPosition( SECONDARY_SELECTION_HANDLE, secondaryX, 0.0f, height );
447 mDecorator->ClearHighlights();
448 mDecorator->AddHighlight( primaryX, 0.0f, secondaryX, height );
452 void ChangeState( State newState )
454 if( mState != newState )
458 if( INACTIVE == mState )
460 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
461 mDecorator->StopCursorBlink();
462 mDecorator->SetGrabHandleActive( false );
463 mDecorator->SetSelectionActive( false );
464 mDecorator->SetPopupActive( false );
465 mDecoratorUpdated = true;
467 else if ( SELECTING == mState )
469 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
470 mDecorator->StopCursorBlink();
471 mDecorator->SetGrabHandleActive( false );
472 mDecorator->SetSelectionActive( true );
473 mDecoratorUpdated = true;
475 else if( EDITING == mState )
477 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
478 if( mCursorBlinkEnabled )
480 mDecorator->StartCursorBlink();
482 if( mGrabHandleEnabled )
484 mDecorator->SetGrabHandleActive( true );
486 if( mGrabHandlePopupEnabled )
488 mDecorator->SetPopupActive( false );
490 mDecorator->SetSelectionActive( false );
491 mDecoratorUpdated = true;
493 else if( EDITING_WITH_POPUP == mState )
495 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
496 if( mCursorBlinkEnabled )
498 mDecorator->StartCursorBlink();
500 if( mGrabHandleEnabled )
502 mDecorator->SetGrabHandleActive( true );
504 if( mGrabHandlePopupEnabled )
506 mDecorator->SetPopupActive( true );
508 mDecorator->SetSelectionActive( false );
509 mDecoratorUpdated = true;
514 LineIndex GetClosestLine( float y ) const
516 float totalHeight = 0.f;
517 LineIndex lineIndex = 0u;
519 const Vector<LineRun>& lines = mVisualModel->mLines;
520 for( LineIndex endLine = lines.Count();
524 const LineRun& lineRun = lines[lineIndex];
525 totalHeight += lineRun.ascender + -lineRun.descender;
526 if( y < totalHeight )
536 * @brief Retrieves the cursor's logical position for a given touch point x,y
538 * @param[in] visualX The touch point x.
539 * @param[in] visualY The touch point y.
541 * @return The logical cursor position (in characters). 0 is just before the first character, a value equal to the number of characters is just after the last character.
543 CharacterIndex GetClosestCursorIndex( float visualX,
544 float visualY ) const
546 CharacterIndex logicalIndex = 0u;
548 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
549 const Length numberOfLines = mVisualModel->mLines.Count();
550 if( 0 == numberOfGlyphs ||
556 // Transform to visual model coords
557 visualX -= mScrollPosition.x;
558 visualY -= mScrollPosition.y;
560 // Find which line is closest
561 const LineIndex lineIndex = GetClosestLine( visualY );
562 const LineRun& line = mVisualModel->mLines[lineIndex];
564 // Get the positions of the glyphs.
565 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
566 const Vector2* const positionsBuffer = positions.Begin();
568 // Get the visual to logical conversion tables.
569 const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
570 const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
572 // Get the character to glyph conversion table.
573 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
575 // Get the glyphs per character table.
576 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
578 // If the vector is void, there is no right to left characters.
579 const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
581 const CharacterIndex startCharacter = line.characterRun.characterIndex;
582 const CharacterIndex endCharacter = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
583 DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
585 // Whether there is a hit on a glyph.
586 bool matched = false;
588 // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
589 CharacterIndex visualIndex = startCharacter;
590 for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
592 // The character in logical order.
593 const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
595 // The first glyph for that character in logical order.
596 const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
598 // The number of glyphs for that character
599 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
601 // Get the metrics for the group of glyphs.
602 GlyphMetrics glyphMetrics;
603 GetGlyphsMetrics( glyphLogicalOrderIndex,
607 const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
609 const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
611 if( visualX < glyphX )
618 // Return the logical position of the cursor in characters.
622 visualIndex = endCharacter;
625 return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
629 * @brief Calculates the cursor's position for a given character index in the logical order.
631 * It retrieves as well the line's height and the cursor's height and
632 * if there is a valid alternative cursor, its position and height.
634 * @param[in] logical The logical cursor position (in characters). 0 is just before the first character, a value equal to the number of characters is just after the last character.
635 * @param[out] cursorInfo The line's height, the cursor's height, the cursor's position and whether there is an alternative cursor.
637 void GetCursorPosition( CharacterIndex logical,
638 CursorInfo& cursorInfo ) const
640 // TODO: Check for multiline with \n, etc...
642 // Check if the logical position is the first or the last one of the text.
643 const bool isFirstPosition = 0u == logical;
644 const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
646 if( isFirstPosition && isLastPosition )
648 // There is zero characters. Get the default font.
650 FontId defaultFontId = 0u;
651 if( NULL == mFontDefaults )
653 defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
658 defaultFontId = mFontDefaults->GetFontId( mFontClient );
661 Text::FontMetrics fontMetrics;
662 mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
664 cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
665 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
667 cursorInfo.primaryPosition.x = 0.f;
668 cursorInfo.primaryPosition.y = 0.f;
670 // Nothing else to do.
674 // Get the previous logical index.
675 const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
677 // Decrease the logical index if it's the last one.
683 // Get the direction of the character and the previous one.
684 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
686 CharacterDirection isCurrentRightToLeft = false;
687 CharacterDirection isPreviousRightToLeft = false;
688 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
690 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
691 isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
694 // Get the line where the character is laid-out.
695 const LineRun* modelLines = mVisualModel->mLines.Begin();
697 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
698 const LineRun& line = *( modelLines + lineIndex );
700 // Get the paragraph's direction.
701 const CharacterDirection isRightToLeftParagraph = line.direction;
703 // Check whether there is an alternative position:
705 cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
706 ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
708 // Set the line height.
709 cursorInfo.lineHeight = line.ascender + -line.descender;
711 // Convert the cursor position into the glyph position.
712 CharacterIndex characterIndex = logical;
713 if( cursorInfo.isSecondaryCursor &&
714 ( isRightToLeftParagraph != isCurrentRightToLeft ) )
716 characterIndex = previousLogical;
719 const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
720 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
721 const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
723 // Get the metrics for the group of glyphs.
724 GlyphMetrics glyphMetrics;
725 GetGlyphsMetrics( currentGlyphIndex,
729 float interGlyphAdvance = 0.f;
730 if( !isLastPosition &&
731 ( numberOfCharacters > 1u ) )
733 const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
734 interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
737 // Get the glyph position and x bearing.
738 const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
740 // Set the cursor's height.
741 cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
744 cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
745 cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
749 // The position of the cursor after the last character needs special
750 // care depending on its direction and the direction of the paragraph.
752 if( cursorInfo.isSecondaryCursor )
754 // Need to find the first character after the last character with the paragraph's direction.
755 // i.e l0 l1 l2 r0 r1 should find r0.
757 // TODO: check for more than one line!
758 characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
759 characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
761 const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
762 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
764 const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
766 // Get the metrics for the group of glyphs.
767 GlyphMetrics glyphMetrics;
768 GetGlyphsMetrics( glyphIndex,
772 cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
774 cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
778 if( !isCurrentRightToLeft )
780 cursorInfo.primaryPosition.x += glyphMetrics.advance;
784 cursorInfo.primaryPosition.x -= glyphMetrics.advance;
789 // Set the alternative cursor position.
790 if( cursorInfo.isSecondaryCursor )
792 // Convert the cursor position into the glyph position.
793 const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
794 const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
795 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
797 // Get the glyph position.
798 const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
800 // Get the metrics for the group of glyphs.
801 GlyphMetrics glyphMetrics;
802 GetGlyphsMetrics( previousGlyphIndex,
806 // Set the cursor position and height.
807 cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
808 ( !isLastPosition && isCurrentRightToLeft ) ) ? glyphMetrics.advance : 0.f );
810 cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
812 cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
814 // Update the primary cursor height as well.
815 cursorInfo.primaryCursorHeight *= 0.5f;
820 * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
822 * @param[in] glyphIndex The index to the first glyph.
823 * @param[in] numberOfGlyphs The number of glyphs.
824 * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
826 void GetGlyphsMetrics( GlyphIndex glyphIndex,
827 Length numberOfGlyphs,
828 GlyphMetrics& glyphMetrics ) const
830 const GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
832 const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
834 Text::FontMetrics fontMetrics;
835 mFontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
837 glyphMetrics.fontHeight = fontMetrics.height;
838 glyphMetrics.advance = firstGlyph.advance;
839 glyphMetrics.ascender = fontMetrics.ascender;
840 glyphMetrics.xBearing = firstGlyph.xBearing;
842 for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
844 const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
846 glyphMetrics.advance += glyphInfo.advance;
851 * @brief Calculates the new cursor index.
853 * It takes into account that in some scripts multiple characters can form a glyph and all of them
854 * need to be jumped with one key event.
856 * @param[in] index The initial new index.
858 * @return The new cursor index.
860 CharacterIndex CalculateNewCursorIndex( CharacterIndex index ) const
862 CharacterIndex cursorIndex = mPrimaryCursorPosition;
864 const Script script = mLogicalModel->GetScript( index );
865 const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
866 const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
868 Length numberOfCharacters = 0u;
869 if( TextAbstraction::LATIN == script )
871 // Prevents to jump the whole Latin ligatures like fi, ff, ...
872 numberOfCharacters = 1u;
876 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
877 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
879 while( 0u == numberOfCharacters )
881 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
886 if( index < mPrimaryCursorPosition )
888 cursorIndex -= numberOfCharacters;
892 cursorIndex += numberOfCharacters;
898 void UpdateCursorPosition()
900 CursorInfo cursorInfo;
902 GetCursorPosition( mPrimaryCursorPosition,
905 mDecorator->SetPosition( PRIMARY_CURSOR,
906 cursorInfo.primaryPosition.x,
907 cursorInfo.primaryPosition.y,
908 cursorInfo.primaryCursorHeight,
909 cursorInfo.lineHeight );
911 if( cursorInfo.isSecondaryCursor )
913 mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
914 mDecorator->SetPosition( SECONDARY_CURSOR,
915 cursorInfo.secondaryPosition.x,
916 cursorInfo.secondaryPosition.y,
917 cursorInfo.secondaryCursorHeight,
918 cursorInfo.lineHeight );
922 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
925 mUpdateCursorPosition = false;
926 mDecoratorUpdated = true;
929 LogicalModelPtr mLogicalModel;
930 VisualModelPtr mVisualModel;
931 DecoratorPtr mDecorator;
932 FontDefaults* mFontDefaults;
933 TextAbstraction::FontClient& mFontClient;
934 std::string mPlaceholderText;
937 * This is used to delay handling events until after the model has been updated.
938 * The number of updates to the model is minimized to improve performance.
940 vector<Event> mEventQueue; ///< The queue of touch events etc.
942 State mState; ///< Selection mode, edit mode etc.
944 CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
945 CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
948 * 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
949 * Typically this will have a negative value with scrolling occurs.
951 Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
953 bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
954 bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
955 bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled
956 bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown
957 bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled
958 bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
959 bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled
960 bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated
963 struct Controller::Impl
965 Impl( ControlInterface& controlInterface )
966 : mControlInterface( controlInterface ),
969 mFontDefaults( NULL ),
977 mOperationsPending( NO_OPERATION ),
978 mRecalculateNaturalSize( true )
980 mLogicalModel = LogicalModel::New();
981 mVisualModel = VisualModel::New();
983 mFontClient = TextAbstraction::FontClient::Get();
985 mView.SetVisualModel( mVisualModel );
987 // Set the text properties to default
988 mVisualModel->SetTextColor( Color::WHITE );
989 mVisualModel->SetShadowOffset( Vector2::ZERO );
990 mVisualModel->SetShadowColor( Color::BLACK );
991 mVisualModel->SetUnderlineEnabled( false );
992 mVisualModel->SetUnderlineHeight( 0.0f );
1000 ControlInterface& mControlInterface; ///< Reference to the text controller.
1001 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
1002 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
1003 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
1004 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
1005 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
1006 View mView; ///< The view interface to the rendering back-end.
1007 LayoutEngine mLayoutEngine; ///< The layout engine.
1008 std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
1009 Size mControlSize; ///< The size of the control.
1010 Vector2 mAlignmentOffset; ///< Vertical and horizontal offset of the whole text inside the control due to alignment.
1011 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
1012 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
1015 ControllerPtr Controller::New( ControlInterface& controlInterface )
1017 return ControllerPtr( new Controller( controlInterface ) );
1020 void Controller::SetText( const std::string& text )
1022 // Cancel previously queued inserts etc.
1023 mImpl->mModifyEvents.clear();
1025 // Keep until size negotiation
1027 event.type = REPLACE_TEXT;
1029 mImpl->mModifyEvents.push_back( event );
1031 if( mImpl->mTextInput )
1033 // Cancel previously queued events
1034 mImpl->mTextInput->mEventQueue.clear();
1036 // TODO - Hide selection decorations
1040 void Controller::GetText( std::string& text ) const
1042 if( !mImpl->mModifyEvents.empty() &&
1043 REPLACE_TEXT == mImpl->mModifyEvents[0].type )
1045 text = mImpl->mModifyEvents[0].text;
1049 // TODO - Convert from UTF-32
1053 void Controller::SetPlaceholderText( const std::string& text )
1055 if( !mImpl->mTextInput )
1057 mImpl->mTextInput->mPlaceholderText = text;
1061 void Controller::GetPlaceholderText( std::string& text ) const
1063 if( !mImpl->mTextInput )
1065 text = mImpl->mTextInput->mPlaceholderText;
1069 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
1071 if( !mImpl->mFontDefaults )
1073 mImpl->mFontDefaults = new Controller::FontDefaults();
1076 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
1077 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1078 mImpl->mOperationsPending = ALL_OPERATIONS;
1079 mImpl->mRecalculateNaturalSize = true;
1081 // Clear the font-specific data
1082 mImpl->mLogicalModel->mFontRuns.Clear();
1083 mImpl->mVisualModel->mGlyphs.Clear();
1084 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1085 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1086 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1087 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1088 mImpl->mVisualModel->mGlyphPositions.Clear();
1089 mImpl->mVisualModel->mLines.Clear();
1090 mImpl->mVisualModel->ClearCaches();
1095 const std::string& Controller::GetDefaultFontFamily() const
1097 if( mImpl->mFontDefaults )
1099 return mImpl->mFontDefaults->mDefaultFontFamily;
1102 return EMPTY_STRING;
1105 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
1107 if( !mImpl->mFontDefaults )
1109 mImpl->mFontDefaults = new Controller::FontDefaults();
1112 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
1113 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1114 mImpl->mOperationsPending = ALL_OPERATIONS;
1115 mImpl->mRecalculateNaturalSize = true;
1117 // Clear the font-specific data
1118 mImpl->mLogicalModel->mFontRuns.Clear();
1119 mImpl->mVisualModel->mGlyphs.Clear();
1120 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1121 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1122 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1123 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1124 mImpl->mVisualModel->mGlyphPositions.Clear();
1125 mImpl->mVisualModel->mLines.Clear();
1126 mImpl->mVisualModel->ClearCaches();
1131 const std::string& Controller::GetDefaultFontStyle() const
1133 if( mImpl->mFontDefaults )
1135 return mImpl->mFontDefaults->mDefaultFontStyle;
1138 return EMPTY_STRING;
1141 void Controller::SetDefaultPointSize( float pointSize )
1143 if( !mImpl->mFontDefaults )
1145 mImpl->mFontDefaults = new Controller::FontDefaults();
1148 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
1149 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1150 mImpl->mOperationsPending = ALL_OPERATIONS;
1151 mImpl->mRecalculateNaturalSize = true;
1153 // Clear the font-specific data
1154 mImpl->mLogicalModel->mFontRuns.Clear();
1155 mImpl->mVisualModel->mGlyphs.Clear();
1156 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1157 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1158 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1159 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1160 mImpl->mVisualModel->mGlyphPositions.Clear();
1161 mImpl->mVisualModel->mLines.Clear();
1162 mImpl->mVisualModel->ClearCaches();
1167 float Controller::GetDefaultPointSize() const
1169 if( mImpl->mFontDefaults )
1171 return mImpl->mFontDefaults->mDefaultPointSize;
1177 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters ) const
1179 if( mImpl->mFontDefaults )
1182 fontRun.characterRun.characterIndex = 0;
1183 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
1184 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
1185 fontRun.isDefault = true;
1187 fonts.PushBack( fontRun );
1191 const Vector4& Controller::GetTextColor() const
1193 return mImpl->mVisualModel->GetTextColor();
1196 const Vector2& Controller::GetShadowOffset() const
1198 return mImpl->mVisualModel->GetShadowOffset();
1201 const Vector4& Controller::GetShadowColor() const
1203 return mImpl->mVisualModel->GetShadowColor();
1206 const Vector4& Controller::GetUnderlineColor() const
1208 return mImpl->mVisualModel->GetUnderlineColor();
1211 bool Controller::IsUnderlineEnabled() const
1213 return mImpl->mVisualModel->IsUnderlineEnabled();
1216 float Controller::GetUnderlineHeight() const
1218 return mImpl->mVisualModel->GetUnderlineHeight();
1221 void Controller::SetTextColor( const Vector4& textColor )
1223 mImpl->mVisualModel->SetTextColor( textColor );
1226 void Controller::SetShadowOffset( const Vector2& shadowOffset )
1228 mImpl->mVisualModel->SetShadowOffset( shadowOffset );
1231 void Controller::SetShadowColor( const Vector4& shadowColor )
1233 mImpl->mVisualModel->SetShadowColor( shadowColor );
1236 void Controller::SetUnderlineColor( const Vector4& color )
1238 mImpl->mVisualModel->SetUnderlineColor( color );
1241 void Controller::SetUnderlineEnabled( bool enabled )
1243 mImpl->mVisualModel->SetUnderlineEnabled( enabled );
1246 void Controller::SetUnderlineHeight( float height )
1248 mImpl->mVisualModel->SetUnderlineHeight( height );
1251 void Controller::EnableTextInput( DecoratorPtr decorator )
1253 if( !mImpl->mTextInput )
1255 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel,
1256 mImpl->mVisualModel,
1258 mImpl->mFontDefaults,
1259 mImpl->mFontClient );
1263 void Controller::SetEnableCursorBlink( bool enable )
1265 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
1267 if( mImpl->mTextInput )
1269 mImpl->mTextInput->mCursorBlinkEnabled = enable;
1272 mImpl->mTextInput->mDecorator )
1274 mImpl->mTextInput->mDecorator->StopCursorBlink();
1279 bool Controller::GetEnableCursorBlink() const
1281 if( mImpl->mTextInput )
1283 return mImpl->mTextInput->mCursorBlinkEnabled;
1289 const Vector2& Controller::GetScrollPosition() const
1291 if( mImpl->mTextInput )
1293 return mImpl->mTextInput->mScrollPosition;
1296 return Vector2::ZERO;
1299 const Vector2& Controller::GetAlignmentOffset() const
1301 return mImpl->mAlignmentOffset;
1304 Vector3 Controller::GetNaturalSize()
1306 Vector3 naturalSize;
1308 // Make sure the model is up-to-date before layouting
1309 ProcessModifyEvents();
1311 if( mImpl->mRecalculateNaturalSize )
1313 // Operations that can be done only once until the text changes.
1314 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1321 GET_GLYPH_METRICS );
1322 // Make sure the model is up-to-date before layouting
1323 UpdateModel( onlyOnceOperations );
1325 // Operations that need to be done if the size changes.
1326 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
1330 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
1331 static_cast<OperationsMask>( onlyOnceOperations |
1333 naturalSize.GetVectorXY() );
1335 // Do not do again the only once operations.
1336 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1338 // Do the size related operations again.
1339 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1341 // Stores the natural size to avoid recalculate it again
1342 // unless the text/style changes.
1343 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
1345 mImpl->mRecalculateNaturalSize = false;
1349 naturalSize = mImpl->mVisualModel->GetNaturalSize();
1355 float Controller::GetHeightForWidth( float width )
1357 // Make sure the model is up-to-date before layouting
1358 ProcessModifyEvents();
1361 if( width != mImpl->mControlSize.width )
1363 // Operations that can be done only once until the text changes.
1364 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1371 GET_GLYPH_METRICS );
1372 // Make sure the model is up-to-date before layouting
1373 UpdateModel( onlyOnceOperations );
1375 // Operations that need to be done if the size changes.
1376 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
1380 DoRelayout( Size( width, MAX_FLOAT ),
1381 static_cast<OperationsMask>( onlyOnceOperations |
1385 // Do not do again the only once operations.
1386 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1388 // Do the size related operations again.
1389 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1393 layoutSize = mImpl->mVisualModel->GetActualSize();
1396 return layoutSize.height;
1399 bool Controller::Relayout( const Size& size )
1401 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
1403 bool glyphsRemoved( false );
1404 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
1406 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
1407 glyphsRemoved = true;
1410 // Not worth to relayout if width or height is equal to zero.
1411 return glyphsRemoved;
1414 if( size != mImpl->mControlSize )
1416 // Operations that need to be done if the size changes.
1417 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1420 UPDATE_ACTUAL_SIZE |
1423 mImpl->mControlSize = size;
1426 // Make sure the model is up-to-date before layouting
1427 ProcessModifyEvents();
1428 UpdateModel( mImpl->mOperationsPending );
1431 bool updated = DoRelayout( mImpl->mControlSize,
1432 mImpl->mOperationsPending,
1435 // Do not re-do any operation until something changes.
1436 mImpl->mOperationsPending = NO_OPERATION;
1438 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
1439 CalculateTextAlignment( size );
1441 if( mImpl->mTextInput )
1443 // Move the cursor, grab handle etc.
1444 updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize, mImpl->mAlignmentOffset ) || updated;
1450 void Controller::ProcessModifyEvents()
1452 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
1454 for( unsigned int i=0; i<events.size(); ++i )
1456 if( REPLACE_TEXT == events[0].type )
1458 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
1459 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
1461 ReplaceTextEvent( events[0].text );
1463 else if( INSERT_TEXT == events[0].type )
1465 InsertTextEvent( events[0].text );
1467 else if( DELETE_TEXT == events[0].type )
1473 // Discard temporary text
1477 void Controller::ReplaceTextEvent( const std::string& text )
1480 mImpl->mLogicalModel->mText.Clear();
1481 mImpl->mLogicalModel->mScriptRuns.Clear();
1482 mImpl->mLogicalModel->mFontRuns.Clear();
1483 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1484 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1485 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1486 mImpl->mLogicalModel->mCharacterDirections.Clear();
1487 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1488 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1489 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1490 mImpl->mVisualModel->mGlyphs.Clear();
1491 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1492 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1493 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1494 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1495 mImpl->mVisualModel->mGlyphPositions.Clear();
1496 mImpl->mVisualModel->mLines.Clear();
1497 mImpl->mVisualModel->ClearCaches();
1499 // Convert text into UTF-32
1500 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1501 utf32Characters.Resize( text.size() );
1503 // This is a bit horrible but std::string returns a (signed) char*
1504 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1506 // Transform a text array encoded in utf8 into an array encoded in utf32.
1507 // It returns the actual number of characters.
1508 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1509 utf32Characters.Resize( characterCount );
1511 // Reset the cursor position
1512 if( mImpl->mTextInput )
1514 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
1515 // TODO - handle secondary cursor
1518 // The natural size needs to be re-calculated.
1519 mImpl->mRecalculateNaturalSize = true;
1521 // Apply modifications to the model
1522 mImpl->mOperationsPending = ALL_OPERATIONS;
1523 UpdateModel( ALL_OPERATIONS );
1524 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1526 UPDATE_ACTUAL_SIZE |
1530 void Controller::InsertTextEvent( const std::string& text )
1532 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1534 // TODO - Optimize this
1535 mImpl->mLogicalModel->mScriptRuns.Clear();
1536 mImpl->mLogicalModel->mFontRuns.Clear();
1537 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1538 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1539 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1540 mImpl->mLogicalModel->mCharacterDirections.Clear();
1541 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1542 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1543 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1544 mImpl->mVisualModel->mGlyphs.Clear();
1545 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1546 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1547 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1548 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1549 mImpl->mVisualModel->mGlyphPositions.Clear();
1550 mImpl->mVisualModel->mLines.Clear();
1551 mImpl->mVisualModel->ClearCaches();
1553 // Convert text into UTF-32
1554 Vector<Character> utf32Characters;
1555 utf32Characters.Resize( text.size() );
1557 // This is a bit horrible but std::string returns a (signed) char*
1558 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1560 // Transform a text array encoded in utf8 into an array encoded in utf32.
1561 // It returns the actual number of characters.
1562 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1563 utf32Characters.Resize( characterCount );
1565 // Insert at current cursor position
1566 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1567 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1569 if( cursorIndex < modifyText.Count() )
1571 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1575 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1578 // Advance the cursor position
1581 // The natural size needs to be re-calculated.
1582 mImpl->mRecalculateNaturalSize = true;
1584 // Apply modifications to the model; TODO - Optimize this
1585 mImpl->mOperationsPending = ALL_OPERATIONS;
1586 UpdateModel( ALL_OPERATIONS );
1587 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1589 UPDATE_ACTUAL_SIZE |
1592 // Queue a cursor reposition event; this must wait until after DoRelayout()
1593 mImpl->mTextInput->mUpdateCursorPosition = true;
1596 void Controller::DeleteTextEvent()
1598 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1600 // TODO - Optimize this
1601 mImpl->mLogicalModel->mScriptRuns.Clear();
1602 mImpl->mLogicalModel->mFontRuns.Clear();
1603 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1604 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1605 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1606 mImpl->mLogicalModel->mCharacterDirections.Clear();
1607 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1608 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1609 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1610 mImpl->mVisualModel->mGlyphs.Clear();
1611 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1612 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1613 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1614 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1615 mImpl->mVisualModel->mGlyphPositions.Clear();
1616 mImpl->mVisualModel->mLines.Clear();
1617 mImpl->mVisualModel->ClearCaches();
1619 // Delte at current cursor position
1620 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1621 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1623 if( cursorIndex > 0 &&
1624 cursorIndex-1 < modifyText.Count() )
1626 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1628 // Cursor position retreat
1632 // The natural size needs to be re-calculated.
1633 mImpl->mRecalculateNaturalSize = true;
1635 // Apply modifications to the model; TODO - Optimize this
1636 mImpl->mOperationsPending = ALL_OPERATIONS;
1637 UpdateModel( ALL_OPERATIONS );
1638 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1640 UPDATE_ACTUAL_SIZE |
1643 // Queue a cursor reposition event; this must wait until after DoRelayout()
1644 mImpl->mTextInput->mUpdateCursorPosition = true;
1647 void Controller::UpdateModel( OperationsMask operationsRequired )
1649 // Calculate the operations to be done.
1650 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1652 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1654 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1656 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1657 if( GET_LINE_BREAKS & operations )
1659 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1660 // calculate the bidirectional info for each 'paragraph'.
1661 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1662 // is not shaped together).
1663 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1665 SetLineBreakInfo( utf32Characters,
1669 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1670 if( GET_WORD_BREAKS & operations )
1672 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1673 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1675 SetWordBreakInfo( utf32Characters,
1679 const bool getScripts = GET_SCRIPTS & operations;
1680 const bool validateFonts = VALIDATE_FONTS & operations;
1682 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1683 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1685 if( getScripts || validateFonts )
1687 // Validates the fonts assigned by the application or assigns default ones.
1688 // It makes sure all the characters are going to be rendered by the correct font.
1689 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1693 // Retrieves the scripts used in the text.
1694 multilanguageSupport.SetScripts( utf32Characters,
1701 if( 0u == validFonts.Count() )
1703 // Copy the requested font defaults received via the property system.
1704 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1705 GetDefaultFonts( validFonts, numberOfCharacters );
1708 // Validates the fonts. If there is a character with no assigned font it sets a default one.
1709 // After this call, fonts are validated.
1710 multilanguageSupport.ValidateFonts( utf32Characters,
1716 Vector<Character> mirroredUtf32Characters;
1717 bool textMirrored = false;
1718 if( BIDI_INFO & operations )
1720 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1721 // bidirectional info.
1723 Length numberOfParagraphs = 0u;
1725 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1726 for( Length index = 0u; index < numberOfCharacters; ++index )
1728 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1730 ++numberOfParagraphs;
1734 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1735 bidirectionalInfo.Reserve( numberOfParagraphs );
1737 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1738 SetBidirectionalInfo( utf32Characters,
1741 bidirectionalInfo );
1743 if( 0u != bidirectionalInfo.Count() )
1745 // This paragraph has right to left text. Some characters may need to be mirrored.
1746 // TODO: consider if the mirrored string can be stored as well.
1748 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1750 // Only set the character directions if there is right to left characters.
1751 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
1752 directions.Resize( numberOfCharacters );
1754 GetCharactersDirection( bidirectionalInfo,
1759 // There is no right to left characters. Clear the directions vector.
1760 mImpl->mLogicalModel->mCharacterDirections.Clear();
1765 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1766 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1767 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1768 if( SHAPE_TEXT & operations )
1770 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1772 ShapeText( textToShape,
1777 glyphsToCharactersMap,
1778 charactersPerGlyph );
1780 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1781 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1782 mImpl->mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
1785 const Length numberOfGlyphs = glyphs.Count();
1787 if( GET_GLYPH_METRICS & operations )
1789 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1793 bool Controller::DoRelayout( const Size& size,
1794 OperationsMask operationsRequired,
1797 bool viewUpdated( false );
1799 // Calculate the operations to be done.
1800 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1802 if( LAYOUT & operations )
1804 // Some vectors with data needed to layout and reorder may be void
1805 // after the first time the text has been laid out.
1806 // Fill the vectors again.
1808 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1810 if( 0u == numberOfGlyphs )
1812 // Nothing else to do if there is no glyphs.
1816 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1817 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1818 Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
1819 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1820 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1821 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1823 // Set the layout parameters.
1824 LayoutParameters layoutParameters( size,
1825 mImpl->mLogicalModel->mText.Begin(),
1826 lineBreakInfo.Begin(),
1827 wordBreakInfo.Begin(),
1828 ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
1831 glyphsToCharactersMap.Begin(),
1832 charactersPerGlyph.Begin() );
1834 // The laid-out lines.
1835 // It's not possible to know in how many lines the text is going to be laid-out,
1836 // but it can be resized at least with the number of 'paragraphs' to avoid
1837 // some re-allocations.
1838 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1840 // Delete any previous laid out lines before setting the new ones.
1843 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1844 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1846 // Resize the vector of positions to have the same size than the vector of glyphs.
1847 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1848 glyphPositions.Resize( numberOfGlyphs );
1850 // Update the visual model.
1851 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1858 // Reorder the lines
1859 if( REORDER & operations )
1861 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1863 // Check first if there are paragraphs with bidirectional info.
1864 if( 0u != bidirectionalInfo.Count() )
1867 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1869 // Reorder the lines.
1870 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1871 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1872 ReorderLines( bidirectionalInfo,
1874 lineBidirectionalInfoRuns );
1876 // Set the bidirectional info into the model.
1877 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1878 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1879 numberOfBidirectionalInfoRuns );
1881 // Set the bidirectional info per line into the layout parameters.
1882 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1883 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1885 // Get the character to glyph conversion table and set into the layout.
1886 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1888 // Get the glyphs per character table and set into the layout.
1889 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1891 // Re-layout the text. Reorder those lines with right to left characters.
1892 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1895 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1896 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1897 endIt = lineBidirectionalInfoRuns.End();
1901 BidirectionalLineInfoRun& bidiLineInfo = *it;
1903 free( bidiLineInfo.visualToLogicalMap );
1908 if( ALIGN & operations )
1910 mImpl->mLayoutEngine.Align( layoutParameters,
1916 // Sets the actual size.
1917 if( UPDATE_ACTUAL_SIZE & operations )
1919 mImpl->mVisualModel->SetActualSize( layoutSize );
1925 layoutSize = mImpl->mVisualModel->GetActualSize();
1931 void Controller::CalculateTextAlignment( const Size& size )
1933 // Get the direction of the first character.
1934 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1936 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1938 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1939 LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1940 if( firstParagraphDirection &&
1941 ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1943 if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1945 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1949 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1953 switch( horizontalAlignment )
1955 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1957 mImpl->mAlignmentOffset.x = 0.f;
1960 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1962 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1963 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1966 case LayoutEngine::HORIZONTAL_ALIGN_END:
1968 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1973 const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1974 switch( verticalAlignment )
1976 case LayoutEngine::VERTICAL_ALIGN_TOP:
1978 mImpl->mAlignmentOffset.y = 0.f;
1981 case LayoutEngine::VERTICAL_ALIGN_CENTER:
1983 const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1984 mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1987 case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1989 mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1995 View& Controller::GetView()
1997 return mImpl->mView;
2000 LayoutEngine& Controller::GetLayoutEngine()
2002 return mImpl->mLayoutEngine;
2005 void Controller::RequestRelayout()
2007 mImpl->mControlInterface.RequestTextRelayout();
2010 void Controller::KeyboardFocusGainEvent()
2012 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
2014 if( mImpl->mTextInput )
2016 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
2017 mImpl->mTextInput->mEventQueue.push_back( event );
2023 void Controller::KeyboardFocusLostEvent()
2025 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
2027 if( mImpl->mTextInput )
2029 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
2030 mImpl->mTextInput->mEventQueue.push_back( event );
2036 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
2038 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
2040 if( mImpl->mTextInput &&
2041 keyEvent.state == KeyEvent::Down )
2043 int keyCode = keyEvent.keyCode;
2044 const std::string& keyString = keyEvent.keyPressed;
2046 // Pre-process to separate modifying events from non-modifying input events.
2047 if( Dali::DALI_KEY_ESCAPE == keyCode )
2049 // Escape key is a special case which causes focus loss
2050 KeyboardFocusLostEvent();
2052 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
2053 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
2054 Dali::DALI_KEY_CURSOR_UP == keyCode ||
2055 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
2057 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
2058 event.p1.mInt = keyCode;
2059 mImpl->mTextInput->mEventQueue.push_back( event );
2061 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
2063 // Queue a delete event
2065 event.type = DELETE_TEXT;
2066 mImpl->mModifyEvents.push_back( event );
2068 else if( !keyString.empty() )
2070 // Queue an insert event
2072 event.type = INSERT_TEXT;
2073 event.text = keyString;
2074 mImpl->mModifyEvents.push_back( event );
2077 mImpl->mTextInput->ChangeState( TextInput::EDITING ); // todo Confirm this is the best place to change the state of
2085 void Controller::TapEvent( unsigned int tapCount, float x, float y )
2087 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
2089 if( mImpl->mTextInput )
2091 TextInput::Event event( TextInput::TAP_EVENT );
2092 event.p1.mUint = tapCount;
2093 event.p2.mFloat = x;
2094 event.p3.mFloat = y;
2095 mImpl->mTextInput->mEventQueue.push_back( event );
2101 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
2103 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
2105 if( mImpl->mTextInput )
2107 TextInput::Event event( TextInput::PAN_EVENT );
2108 event.p1.mInt = state;
2109 event.p2.mFloat = displacement.x;
2110 event.p3.mFloat = displacement.y;
2111 mImpl->mTextInput->mEventQueue.push_back( event );
2117 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
2119 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
2121 if( mImpl->mTextInput )
2123 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
2124 event.p1.mUint = state;
2125 event.p2.mFloat = x;
2126 event.p3.mFloat = y;
2127 mImpl->mTextInput->mEventQueue.push_back( event );
2133 Controller::~Controller()
2138 Controller::Controller( ControlInterface& controlInterface )
2141 mImpl = new Controller::Impl( controlInterface );
2146 } // namespace Toolkit