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
72 struct Controller::FontDefaults
75 : mDefaultPointSize(0.0f),
80 FontId GetFontId( TextAbstraction::FontClient& fontClient )
84 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
85 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
91 std::string mDefaultFontFamily;
92 std::string mDefaultFontStyle;
93 float mDefaultPointSize;
97 struct Controller::TextInput
99 // Used to queue input events until DoRelayout()
102 KEYBOARD_FOCUS_GAIN_EVENT,
103 KEYBOARD_FOCUS_LOST_EVENT,
119 Event( EventType eventType )
138 primaryCursorHeight( 0.f ),
139 secondaryCursorHeight( 0.f ),
140 isSecondaryCursor( false )
146 Vector2 primaryPosition; ///< The primary cursor's position.
147 Vector2 secondaryPosition; ///< The secondary cursor's position.
148 float lineHeight; ///< The height of the line where the cursor is placed.
149 float primaryCursorHeight; ///< The primary cursor's height.
150 float secondaryCursorHeight; ///< The secondary cursor's height.
151 bool isSecondaryCursor; ///< Whether the secondary cursor is valid.
155 * @brief Some characters can be shaped in more than one glyph.
156 * This struct is used to retrieve metrics from these group of glyphs.
170 float fontHeight; ///< The font's height of that glyphs.
171 float advance; ///< The sum of all the advances of all the glyphs.
172 float ascender; ///< The font's ascender.
173 float xBearing; ///< The x bearing of the first glyph.
184 TextInput( LogicalModelPtr logicalModel,
185 VisualModelPtr visualModel,
186 DecoratorPtr decorator,
187 FontDefaults* fontDefaults,
188 TextAbstraction::FontClient& fontClient )
189 : mLogicalModel( logicalModel ),
190 mVisualModel( visualModel ),
191 mDecorator( decorator ),
192 mFontDefaults( fontDefaults ),
193 mFontClient( fontClient ),
195 mPrimaryCursorPosition( 0u ),
196 mSecondaryCursorPosition( 0u ),
197 mDecoratorUpdated( false ),
198 mCursorBlinkEnabled( true ),
199 mGrabHandleEnabled( true ),
200 mGrabHandlePopupEnabled( true ),
201 mSelectionEnabled( true ),
202 mHorizontalScrollingEnabled( true ),
203 mVerticalScrollingEnabled( false ),
204 mUpdateCursorPosition( false )
208 * @brief Helper to move the cursor, grab handle etc.
210 bool ProcessInputEvents( const Vector2& controlSize,
211 const Vector2& alignmentOffset )
213 mDecoratorUpdated = false;
217 for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
221 case KEYBOARD_FOCUS_GAIN_EVENT:
223 OnKeyboardFocus( true );
226 case KEYBOARD_FOCUS_LOST_EVENT:
228 OnKeyboardFocus( false );
231 case CURSOR_KEY_EVENT:
233 OnCursorKeyEvent( *iter );
238 OnTapEvent( *iter, alignmentOffset );
243 OnPanEvent( *iter, controlSize, alignmentOffset );
246 case GRAB_HANDLE_EVENT:
248 OnGrabHandleEvent( *iter );
255 // The cursor must also be repositioned after inserts into the model
256 if( mUpdateCursorPosition )
258 UpdateCursorPosition();
259 mUpdateCursorPosition = false;
264 return mDecoratorUpdated;
267 void OnKeyboardFocus( bool hasFocus )
271 ChangeState( INACTIVE );
275 ChangeState( EDITING );
279 void OnCursorKeyEvent( const Event& event )
281 int keyCode = event.p1.mInt;
283 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
285 if( mPrimaryCursorPosition > 0u )
287 mPrimaryCursorPosition = CalculateNewCursorIndex( mPrimaryCursorPosition - 1u );
290 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
292 if( mLogicalModel->GetNumberOfCharacters() > mPrimaryCursorPosition )
294 mPrimaryCursorPosition = CalculateNewCursorIndex( mPrimaryCursorPosition );
297 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
301 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
306 UpdateCursorPosition();
309 void HandleCursorKey( int keyCode )
314 void OnTapEvent( const Event& event,
315 const Vector2& alignmentOffset )
317 unsigned int tapCount = event.p1.mUint;
321 ChangeState( EDITING );
323 float xPosition = event.p2.mFloat - alignmentOffset.x;
324 float yPosition = event.p3.mFloat - alignmentOffset.y;
326 mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
329 UpdateCursorPosition();
331 else if( mSelectionEnabled &&
334 ChangeState( SELECTING );
336 RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
340 void OnPanEvent( const Event& event,
341 const Vector2& controlSize,
342 const Vector2& alignmentOffset )
344 int state = event.p1.mInt;
346 if( Gesture::Started == state ||
347 Gesture::Continuing == state )
349 const Vector2& actualSize = mVisualModel->GetActualSize();
351 if( mHorizontalScrollingEnabled )
353 const float displacementX = event.p2.mFloat;
354 mScrollPosition.x += displacementX;
356 // Clamp between -space & 0 (and the text alignment).
357 const float contentWidth = actualSize.width;
358 if( contentWidth > controlSize.width )
360 const float space = ( contentWidth - controlSize.width ) + alignmentOffset.x;
361 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
362 mScrollPosition.x = ( mScrollPosition.x > -alignmentOffset.x ) ? -alignmentOffset.x : mScrollPosition.x;
364 mDecoratorUpdated = true;
368 mScrollPosition.x = 0.f;
372 if( mVerticalScrollingEnabled )
374 const float displacementY = event.p3.mFloat;
375 mScrollPosition.y += displacementY;
377 // Clamp between -space & 0 (and the text alignment).
378 if( actualSize.height > controlSize.height )
380 const float space = ( actualSize.height - controlSize.height ) + alignmentOffset.y;
381 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
382 mScrollPosition.y = ( mScrollPosition.y > -alignmentOffset.y ) ? -alignmentOffset.y : mScrollPosition.y;
384 mDecoratorUpdated = true;
388 mScrollPosition.y = 0.f;
394 void OnGrabHandleEvent( const Event& event )
396 unsigned int state = event.p1.mUint;
398 if( GRAB_HANDLE_PRESSED == state )
400 float xPosition = event.p2.mFloat + mScrollPosition.x;
401 float yPosition = event.p3.mFloat + mScrollPosition.y;
403 mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
406 UpdateCursorPosition();
408 //mDecorator->HidePopup();
409 ChangeState ( EDITING );
411 else if ( mGrabHandlePopupEnabled &&
412 GRAB_HANDLE_RELEASED == state )
414 //mDecorator->ShowPopup();
415 ChangeState ( EDITING_WITH_POPUP );
416 mDecoratorUpdated = true;
420 void RepositionSelectionHandles( float visualX, float visualY )
422 // TODO - Find which word was selected
424 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
425 const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
427 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
428 const Vector<Vector2>::SizeType positionCount = positions.Count();
430 // Guard against glyphs which did not fit inside the layout
431 const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
435 float primaryX = positions[0].x;
436 float secondaryX = positions[count-1].x + glyphs[count-1].width;
438 // TODO - multi-line selection
439 const Vector<LineRun>& lines = mVisualModel->mLines;
440 float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
442 mDecorator->SetPosition( PRIMARY_SELECTION_HANDLE, primaryX, 0.0f, height );
443 mDecorator->SetPosition( SECONDARY_SELECTION_HANDLE, secondaryX, 0.0f, height );
445 mDecorator->ClearHighlights();
446 mDecorator->AddHighlight( primaryX, 0.0f, secondaryX, height );
450 void ChangeState( State newState )
452 if( mState != newState )
456 if( INACTIVE == mState )
458 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
459 mDecorator->StopCursorBlink();
460 mDecorator->SetGrabHandleActive( false );
461 mDecorator->SetSelectionActive( false );
462 mDecorator->SetPopupActive( false );
463 mDecoratorUpdated = true;
465 else if ( SELECTING == mState )
467 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
468 mDecorator->StopCursorBlink();
469 mDecorator->SetGrabHandleActive( false );
470 mDecorator->SetSelectionActive( true );
471 mDecoratorUpdated = true;
473 else if( EDITING == mState )
475 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
476 if( mCursorBlinkEnabled )
478 mDecorator->StartCursorBlink();
480 if( mGrabHandleEnabled )
482 mDecorator->SetGrabHandleActive( true );
484 if( mGrabHandlePopupEnabled )
486 mDecorator->SetPopupActive( false );
488 mDecorator->SetSelectionActive( false );
489 mDecoratorUpdated = true;
491 else if( EDITING_WITH_POPUP == mState )
493 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
494 if( mCursorBlinkEnabled )
496 mDecorator->StartCursorBlink();
498 if( mGrabHandleEnabled )
500 mDecorator->SetGrabHandleActive( true );
502 if( mGrabHandlePopupEnabled )
504 mDecorator->SetPopupActive( true );
506 mDecorator->SetSelectionActive( false );
507 mDecoratorUpdated = true;
512 LineIndex GetClosestLine( float y ) const
514 float totalHeight = 0.f;
515 LineIndex lineIndex = 0u;
517 const Vector<LineRun>& lines = mVisualModel->mLines;
518 for( LineIndex endLine = lines.Count();
522 const LineRun& lineRun = lines[lineIndex];
523 totalHeight += lineRun.ascender + -lineRun.descender;
524 if( y < totalHeight )
534 * @brief Retrieves the cursor's logical position for a given touch point x,y
536 * @param[in] visualX The touch point x.
537 * @param[in] visualY The touch point y.
539 * @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.
541 CharacterIndex GetClosestCursorIndex( float visualX,
542 float visualY ) const
544 CharacterIndex logicalIndex = 0u;
546 const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
547 const Length numberOfLines = mVisualModel->mLines.Count();
548 if( 0 == numberOfGlyphs ||
554 // Transform to visual model coords
555 visualX -= mScrollPosition.x;
556 visualY -= mScrollPosition.y;
558 // Find which line is closest
559 const LineIndex lineIndex = GetClosestLine( visualY );
560 const LineRun& line = mVisualModel->mLines[lineIndex];
562 // Get the positions of the glyphs.
563 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
564 const Vector2* const positionsBuffer = positions.Begin();
566 // Get the visual to logical conversion tables.
567 const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
568 const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
570 // Get the character to glyph conversion table.
571 const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
573 // Get the glyphs per character table.
574 const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
576 // If the vector is void, there is no right to left characters.
577 const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
579 const CharacterIndex startCharacter = line.characterRun.characterIndex;
580 const CharacterIndex endCharacter = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
581 DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
583 // Whether there is a hit on a glyph.
584 bool matched = false;
586 // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
587 CharacterIndex visualIndex = startCharacter;
588 for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
590 // The character in logical order.
591 const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
593 // The first glyph for that character in logical order.
594 const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
596 // The number of glyphs for that character
597 const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
599 // Get the metrics for the group of glyphs.
600 GlyphMetrics glyphMetrics;
601 GetGlyphsMetrics( glyphLogicalOrderIndex,
605 const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
607 const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
609 if( visualX < glyphX )
616 // Return the logical position of the cursor in characters.
620 visualIndex = endCharacter;
623 return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
627 * @brief Calculates the cursor's position for a given character index in the logical order.
629 * It retrieves as well the line's height and the cursor's height and
630 * if there is a valid alternative cursor, its position and height.
632 * @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.
633 * @param[out] cursorInfo The line's height, the cursor's height, the cursor's position and whether there is an alternative cursor.
635 void GetCursorPosition( CharacterIndex logical,
636 CursorInfo& cursorInfo ) const
638 // TODO: Check for multiline with \n, etc...
640 // Check if the logical position is the first or the last one of the text.
641 const bool isFirstPosition = 0u == logical;
642 const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
644 if( isFirstPosition && isLastPosition )
646 // There is zero characters. Get the default font.
648 FontId defaultFontId = 0u;
649 if( NULL == mFontDefaults )
651 defaultFontId = mFontClient.GetFontId( String::EMPTY,
656 defaultFontId = mFontDefaults->GetFontId( mFontClient );
659 Text::FontMetrics fontMetrics;
660 mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
662 cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
663 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
665 cursorInfo.primaryPosition.x = 0.f;
666 cursorInfo.primaryPosition.y = 0.f;
668 // Nothing else to do.
672 // Get the previous logical index.
673 const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
675 // Decrease the logical index if it's the last one.
681 // Get the direction of the character and the previous one.
682 const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
684 CharacterDirection isCurrentRightToLeft = false;
685 CharacterDirection isPreviousRightToLeft = false;
686 if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
688 isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
689 isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
692 // Get the line where the character is laid-out.
693 const LineRun* modelLines = mVisualModel->mLines.Begin();
695 const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
696 const LineRun& line = *( modelLines + lineIndex );
698 // Get the paragraph's direction.
699 const CharacterDirection isRightToLeftParagraph = line.direction;
701 // Check whether there is an alternative position:
703 cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
704 ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
706 // Set the line height.
707 cursorInfo.lineHeight = line.ascender + -line.descender;
709 // Convert the cursor position into the glyph position.
710 CharacterIndex characterIndex = logical;
711 if( cursorInfo.isSecondaryCursor &&
712 ( isRightToLeftParagraph != isCurrentRightToLeft ) )
714 characterIndex = previousLogical;
717 const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
718 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
719 const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
721 // Get the metrics for the group of glyphs.
722 GlyphMetrics glyphMetrics;
723 GetGlyphsMetrics( currentGlyphIndex,
727 float interGlyphAdvance = 0.f;
728 if( !isLastPosition &&
729 ( numberOfCharacters > 1u ) )
731 const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
732 interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
735 // Get the glyph position and x bearing.
736 const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
738 // Set the cursor's height.
739 cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
742 cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
743 cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
747 // The position of the cursor after the last character needs special
748 // care depending on its direction and the direction of the paragraph.
750 if( cursorInfo.isSecondaryCursor )
752 // Need to find the first character after the last character with the paragraph's direction.
753 // i.e l0 l1 l2 r0 r1 should find r0.
755 // TODO: check for more than one line!
756 characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
757 characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
759 const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
760 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
762 const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
764 // Get the metrics for the group of glyphs.
765 GlyphMetrics glyphMetrics;
766 GetGlyphsMetrics( glyphIndex,
770 cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
772 cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
776 if( !isCurrentRightToLeft )
778 cursorInfo.primaryPosition.x += glyphMetrics.advance;
782 cursorInfo.primaryPosition.x -= glyphMetrics.advance;
787 // Set the alternative cursor position.
788 if( cursorInfo.isSecondaryCursor )
790 // Convert the cursor position into the glyph position.
791 const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
792 const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
793 const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
795 // Get the glyph position.
796 const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
798 // Get the metrics for the group of glyphs.
799 GlyphMetrics glyphMetrics;
800 GetGlyphsMetrics( previousGlyphIndex,
804 // Set the cursor position and height.
805 cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
806 ( !isLastPosition && isCurrentRightToLeft ) ) ? glyphMetrics.advance : 0.f );
808 cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
810 cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
812 // Update the primary cursor height as well.
813 cursorInfo.primaryCursorHeight *= 0.5f;
818 * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
820 * @param[in] glyphIndex The index to the first glyph.
821 * @param[in] numberOfGlyphs The number of glyphs.
822 * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
824 void GetGlyphsMetrics( GlyphIndex glyphIndex,
825 Length numberOfGlyphs,
826 GlyphMetrics& glyphMetrics ) const
828 const GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
830 const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
832 Text::FontMetrics fontMetrics;
833 mFontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
835 glyphMetrics.fontHeight = fontMetrics.height;
836 glyphMetrics.advance = firstGlyph.advance;
837 glyphMetrics.ascender = fontMetrics.ascender;
838 glyphMetrics.xBearing = firstGlyph.xBearing;
840 for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
842 const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
844 glyphMetrics.advance += glyphInfo.advance;
849 * @brief Calculates the new cursor index.
851 * It takes into account that in some scripts multiple characters can form a glyph and all of them
852 * need to be jumped with one key event.
854 * @param[in] index The initial new index.
856 * @return The new cursor index.
858 CharacterIndex CalculateNewCursorIndex( CharacterIndex index ) const
860 CharacterIndex cursorIndex = mPrimaryCursorPosition;
862 const Script script = mLogicalModel->GetScript( index );
863 const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
864 const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
866 Length numberOfCharacters = 0u;
867 if( TextAbstraction::LATIN == script )
869 // Prevents to jump the whole Latin ligatures like fi, ff, ...
870 numberOfCharacters = 1u;
874 GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
875 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
877 while( 0u == numberOfCharacters )
879 numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
884 if( index < mPrimaryCursorPosition )
886 cursorIndex -= numberOfCharacters;
890 cursorIndex += numberOfCharacters;
896 void UpdateCursorPosition()
898 CursorInfo cursorInfo;
900 GetCursorPosition( mPrimaryCursorPosition,
903 mDecorator->SetPosition( PRIMARY_CURSOR,
904 cursorInfo.primaryPosition.x,
905 cursorInfo.primaryPosition.y,
906 cursorInfo.primaryCursorHeight,
907 cursorInfo.lineHeight );
909 if( cursorInfo.isSecondaryCursor )
911 mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
912 mDecorator->SetPosition( SECONDARY_CURSOR,
913 cursorInfo.secondaryPosition.x,
914 cursorInfo.secondaryPosition.y,
915 cursorInfo.secondaryCursorHeight,
916 cursorInfo.lineHeight );
920 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
923 mUpdateCursorPosition = false;
924 mDecoratorUpdated = true;
927 LogicalModelPtr mLogicalModel;
928 VisualModelPtr mVisualModel;
929 DecoratorPtr mDecorator;
930 FontDefaults* mFontDefaults;
931 TextAbstraction::FontClient& mFontClient;
932 std::string mPlaceholderText;
935 * This is used to delay handling events until after the model has been updated.
936 * The number of updates to the model is minimized to improve performance.
938 vector<Event> mEventQueue; ///< The queue of touch events etc.
940 State mState; ///< Selection mode, edit mode etc.
942 CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
943 CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
946 * 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
947 * Typically this will have a negative value with scrolling occurs.
949 Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
951 bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
952 bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
953 bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled
954 bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown
955 bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled
956 bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
957 bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled
958 bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated
961 struct Controller::Impl
963 Impl( ControlInterface& controlInterface )
964 : mControlInterface( controlInterface ),
967 mFontDefaults( NULL ),
975 mOperationsPending( NO_OPERATION ),
976 mRecalculateNaturalSize( true )
978 mLogicalModel = LogicalModel::New();
979 mVisualModel = VisualModel::New();
981 mFontClient = TextAbstraction::FontClient::Get();
983 mView.SetVisualModel( mVisualModel );
985 // Set the text properties to default
986 mVisualModel->SetTextColor( Color::WHITE );
987 mVisualModel->SetShadowOffset( Vector2::ZERO );
988 mVisualModel->SetShadowColor( Vector4::ZERO );
989 mVisualModel->SetUnderlineEnabled( false );
997 ControlInterface& mControlInterface; ///< Reference to the text controller.
998 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
999 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
1000 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
1001 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
1002 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
1003 View mView; ///< The view interface to the rendering back-end.
1004 LayoutEngine mLayoutEngine; ///< The layout engine.
1005 std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
1006 Size mControlSize; ///< The size of the control.
1007 Vector2 mAlignmentOffset; ///< Vertical and horizontal offset of the whole text inside the control due to alignment.
1008 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
1009 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
1012 ControllerPtr Controller::New( ControlInterface& controlInterface )
1014 return ControllerPtr( new Controller( controlInterface ) );
1017 void Controller::SetText( const std::string& text )
1019 // Cancel previously queued inserts etc.
1020 mImpl->mModifyEvents.clear();
1022 // Keep until size negotiation
1024 event.type = REPLACE_TEXT;
1026 mImpl->mModifyEvents.push_back( event );
1028 if( mImpl->mTextInput )
1030 // Cancel previously queued events
1031 mImpl->mTextInput->mEventQueue.clear();
1033 // TODO - Hide selection decorations
1037 void Controller::GetText( std::string& text ) const
1039 if( !mImpl->mModifyEvents.empty() &&
1040 REPLACE_TEXT == mImpl->mModifyEvents[0].type )
1042 text = mImpl->mModifyEvents[0].text;
1046 // TODO - Convert from UTF-32
1050 void Controller::SetPlaceholderText( const std::string& text )
1052 if( !mImpl->mTextInput )
1054 mImpl->mTextInput->mPlaceholderText = text;
1058 void Controller::GetPlaceholderText( std::string& text ) const
1060 if( !mImpl->mTextInput )
1062 text = mImpl->mTextInput->mPlaceholderText;
1066 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
1068 if( !mImpl->mFontDefaults )
1070 mImpl->mFontDefaults = new Controller::FontDefaults();
1073 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
1074 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1075 mImpl->mOperationsPending = ALL_OPERATIONS;
1076 mImpl->mRecalculateNaturalSize = true;
1078 // Clear the font-specific data
1079 mImpl->mLogicalModel->mFontRuns.Clear();
1080 mImpl->mVisualModel->mGlyphs.Clear();
1081 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1082 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1083 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1084 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1085 mImpl->mVisualModel->mGlyphPositions.Clear();
1086 mImpl->mVisualModel->mLines.Clear();
1087 mImpl->mVisualModel->ClearCaches();
1092 const std::string& Controller::GetDefaultFontFamily() const
1094 if( mImpl->mFontDefaults )
1096 return mImpl->mFontDefaults->mDefaultFontFamily;
1099 return String::EMPTY;;
1102 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
1104 if( !mImpl->mFontDefaults )
1106 mImpl->mFontDefaults = new Controller::FontDefaults();
1109 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
1110 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1111 mImpl->mOperationsPending = ALL_OPERATIONS;
1112 mImpl->mRecalculateNaturalSize = true;
1114 // Clear the font-specific data
1115 mImpl->mLogicalModel->mFontRuns.Clear();
1116 mImpl->mVisualModel->mGlyphs.Clear();
1117 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1118 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1119 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1120 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1121 mImpl->mVisualModel->mGlyphPositions.Clear();
1122 mImpl->mVisualModel->mLines.Clear();
1123 mImpl->mVisualModel->ClearCaches();
1128 const std::string& Controller::GetDefaultFontStyle() const
1130 if( mImpl->mFontDefaults )
1132 return mImpl->mFontDefaults->mDefaultFontStyle;
1135 return String::EMPTY;
1138 void Controller::SetDefaultPointSize( float pointSize )
1140 if( !mImpl->mFontDefaults )
1142 mImpl->mFontDefaults = new Controller::FontDefaults();
1145 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
1146 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1147 mImpl->mOperationsPending = ALL_OPERATIONS;
1148 mImpl->mRecalculateNaturalSize = true;
1150 // Clear the font-specific data
1151 mImpl->mLogicalModel->mFontRuns.Clear();
1152 mImpl->mVisualModel->mGlyphs.Clear();
1153 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1154 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1155 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1156 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1157 mImpl->mVisualModel->mGlyphPositions.Clear();
1158 mImpl->mVisualModel->mLines.Clear();
1159 mImpl->mVisualModel->ClearCaches();
1164 float Controller::GetDefaultPointSize() const
1166 if( mImpl->mFontDefaults )
1168 return mImpl->mFontDefaults->mDefaultPointSize;
1174 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters ) const
1176 if( mImpl->mFontDefaults )
1179 fontRun.characterRun.characterIndex = 0;
1180 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
1181 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
1182 fontRun.isDefault = true;
1184 fonts.PushBack( fontRun );
1188 const Vector4& Controller::GetTextColor() const
1190 return mImpl->mVisualModel->GetTextColor();
1193 const Vector2& Controller::GetShadowOffset() const
1195 return mImpl->mVisualModel->GetShadowOffset();
1198 const Vector4& Controller::GetShadowColor() const
1200 return mImpl->mVisualModel->GetShadowColor();
1203 const Vector4& Controller::GetUnderlineColor() const
1205 return mImpl->mVisualModel->GetUnderlineColor();
1208 bool Controller::IsUnderlineEnabled() const
1210 return mImpl->mVisualModel->IsUnderlineEnabled();
1213 void Controller::SetTextColor( const Vector4& textColor )
1215 mImpl->mVisualModel->SetTextColor( textColor );
1218 void Controller::SetShadowOffset( const Vector2& shadowOffset )
1220 mImpl->mVisualModel->SetShadowOffset( shadowOffset );
1223 void Controller::SetShadowColor( const Vector4& shadowColor )
1225 mImpl->mVisualModel->SetShadowColor( shadowColor );
1228 void Controller::SetUnderlineColor( const Vector4& color )
1230 mImpl->mVisualModel->SetUnderlineColor( color );
1233 void Controller::SetUnderlineEnabled( bool enabled )
1235 mImpl->mVisualModel->SetUnderlineEnabled( enabled );
1238 void Controller::EnableTextInput( DecoratorPtr decorator )
1240 if( !mImpl->mTextInput )
1242 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel,
1243 mImpl->mVisualModel,
1245 mImpl->mFontDefaults,
1246 mImpl->mFontClient );
1250 void Controller::SetEnableCursorBlink( bool enable )
1252 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
1254 if( mImpl->mTextInput )
1256 mImpl->mTextInput->mCursorBlinkEnabled = enable;
1259 mImpl->mTextInput->mDecorator )
1261 mImpl->mTextInput->mDecorator->StopCursorBlink();
1266 bool Controller::GetEnableCursorBlink() const
1268 if( mImpl->mTextInput )
1270 return mImpl->mTextInput->mCursorBlinkEnabled;
1276 const Vector2& Controller::GetScrollPosition() const
1278 if( mImpl->mTextInput )
1280 return mImpl->mTextInput->mScrollPosition;
1283 return Vector2::ZERO;
1286 const Vector2& Controller::GetAlignmentOffset() const
1288 return mImpl->mAlignmentOffset;
1291 Vector3 Controller::GetNaturalSize()
1293 Vector3 naturalSize;
1295 // Make sure the model is up-to-date before layouting
1296 ProcessModifyEvents();
1298 if( mImpl->mRecalculateNaturalSize )
1300 // Operations that can be done only once until the text changes.
1301 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1308 GET_GLYPH_METRICS );
1309 // Make sure the model is up-to-date before layouting
1310 UpdateModel( onlyOnceOperations );
1312 // Operations that need to be done if the size changes.
1313 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
1317 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
1318 static_cast<OperationsMask>( onlyOnceOperations |
1320 naturalSize.GetVectorXY() );
1322 // Do not do again the only once operations.
1323 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1325 // Do the size related operations again.
1326 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1328 // Stores the natural size to avoid recalculate it again
1329 // unless the text/style changes.
1330 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
1332 mImpl->mRecalculateNaturalSize = false;
1336 naturalSize = mImpl->mVisualModel->GetNaturalSize();
1342 float Controller::GetHeightForWidth( float width )
1344 // Make sure the model is up-to-date before layouting
1345 ProcessModifyEvents();
1348 if( width != mImpl->mControlSize.width )
1350 // Operations that can be done only once until the text changes.
1351 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1358 GET_GLYPH_METRICS );
1359 // Make sure the model is up-to-date before layouting
1360 UpdateModel( onlyOnceOperations );
1362 // Operations that need to be done if the size changes.
1363 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
1367 DoRelayout( Size( width, MAX_FLOAT ),
1368 static_cast<OperationsMask>( onlyOnceOperations |
1372 // Do not do again the only once operations.
1373 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1375 // Do the size related operations again.
1376 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1380 layoutSize = mImpl->mVisualModel->GetActualSize();
1383 return layoutSize.height;
1386 bool Controller::Relayout( const Size& size )
1388 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
1390 bool glyphsRemoved( false );
1391 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
1393 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
1394 glyphsRemoved = true;
1397 // Not worth to relayout if width or height is equal to zero.
1398 return glyphsRemoved;
1401 if( size != mImpl->mControlSize )
1403 // Operations that need to be done if the size changes.
1404 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1407 UPDATE_ACTUAL_SIZE |
1410 mImpl->mControlSize = size;
1413 // Make sure the model is up-to-date before layouting
1414 ProcessModifyEvents();
1415 UpdateModel( mImpl->mOperationsPending );
1418 bool updated = DoRelayout( mImpl->mControlSize,
1419 mImpl->mOperationsPending,
1422 // Do not re-do any operation until something changes.
1423 mImpl->mOperationsPending = NO_OPERATION;
1425 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
1426 CalculateTextAlignment( size );
1428 if( mImpl->mTextInput )
1430 // Move the cursor, grab handle etc.
1431 updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize, mImpl->mAlignmentOffset ) || updated;
1437 void Controller::ProcessModifyEvents()
1439 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
1441 for( unsigned int i=0; i<events.size(); ++i )
1443 if( REPLACE_TEXT == events[0].type )
1445 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
1446 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
1448 ReplaceTextEvent( events[0].text );
1450 else if( INSERT_TEXT == events[0].type )
1452 InsertTextEvent( events[0].text );
1454 else if( DELETE_TEXT == events[0].type )
1460 // Discard temporary text
1464 void Controller::ReplaceTextEvent( const std::string& text )
1467 mImpl->mLogicalModel->mText.Clear();
1468 mImpl->mLogicalModel->mScriptRuns.Clear();
1469 mImpl->mLogicalModel->mFontRuns.Clear();
1470 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1471 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1472 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1473 mImpl->mLogicalModel->mCharacterDirections.Clear();
1474 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1475 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1476 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1477 mImpl->mVisualModel->mGlyphs.Clear();
1478 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1479 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1480 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1481 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1482 mImpl->mVisualModel->mGlyphPositions.Clear();
1483 mImpl->mVisualModel->mLines.Clear();
1484 mImpl->mVisualModel->ClearCaches();
1486 // Convert text into UTF-32
1487 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1488 utf32Characters.Resize( text.size() );
1490 // This is a bit horrible but std::string returns a (signed) char*
1491 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1493 // Transform a text array encoded in utf8 into an array encoded in utf32.
1494 // It returns the actual number of characters.
1495 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1496 utf32Characters.Resize( characterCount );
1498 // Reset the cursor position
1499 if( mImpl->mTextInput )
1501 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
1502 // TODO - handle secondary cursor
1505 // The natural size needs to be re-calculated.
1506 mImpl->mRecalculateNaturalSize = true;
1508 // Apply modifications to the model
1509 mImpl->mOperationsPending = ALL_OPERATIONS;
1510 UpdateModel( ALL_OPERATIONS );
1511 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1513 UPDATE_ACTUAL_SIZE |
1517 void Controller::InsertTextEvent( const std::string& text )
1519 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1521 // TODO - Optimize this
1522 mImpl->mLogicalModel->mScriptRuns.Clear();
1523 mImpl->mLogicalModel->mFontRuns.Clear();
1524 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1525 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1526 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1527 mImpl->mLogicalModel->mCharacterDirections.Clear();
1528 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1529 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1530 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1531 mImpl->mVisualModel->mGlyphs.Clear();
1532 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1533 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1534 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1535 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1536 mImpl->mVisualModel->mGlyphPositions.Clear();
1537 mImpl->mVisualModel->mLines.Clear();
1538 mImpl->mVisualModel->ClearCaches();
1540 // Convert text into UTF-32
1541 Vector<Character> utf32Characters;
1542 utf32Characters.Resize( text.size() );
1544 // This is a bit horrible but std::string returns a (signed) char*
1545 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1547 // Transform a text array encoded in utf8 into an array encoded in utf32.
1548 // It returns the actual number of characters.
1549 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1550 utf32Characters.Resize( characterCount );
1552 // Insert at current cursor position
1553 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1554 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1556 if( cursorIndex < modifyText.Count() )
1558 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1562 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1565 // Advance the cursor position
1568 // The natural size needs to be re-calculated.
1569 mImpl->mRecalculateNaturalSize = true;
1571 // Apply modifications to the model; TODO - Optimize this
1572 mImpl->mOperationsPending = ALL_OPERATIONS;
1573 UpdateModel( ALL_OPERATIONS );
1574 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1576 UPDATE_ACTUAL_SIZE |
1579 // Queue a cursor reposition event; this must wait until after DoRelayout()
1580 mImpl->mTextInput->mUpdateCursorPosition = true;
1583 void Controller::DeleteTextEvent()
1585 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1587 // TODO - Optimize this
1588 mImpl->mLogicalModel->mScriptRuns.Clear();
1589 mImpl->mLogicalModel->mFontRuns.Clear();
1590 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1591 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1592 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1593 mImpl->mLogicalModel->mCharacterDirections.Clear();
1594 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1595 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1596 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1597 mImpl->mVisualModel->mGlyphs.Clear();
1598 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1599 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1600 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1601 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1602 mImpl->mVisualModel->mGlyphPositions.Clear();
1603 mImpl->mVisualModel->mLines.Clear();
1604 mImpl->mVisualModel->ClearCaches();
1606 // Delte at current cursor position
1607 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1608 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1610 if( cursorIndex > 0 &&
1611 cursorIndex-1 < modifyText.Count() )
1613 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1615 // Cursor position retreat
1619 // The natural size needs to be re-calculated.
1620 mImpl->mRecalculateNaturalSize = true;
1622 // Apply modifications to the model; TODO - Optimize this
1623 mImpl->mOperationsPending = ALL_OPERATIONS;
1624 UpdateModel( ALL_OPERATIONS );
1625 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1627 UPDATE_ACTUAL_SIZE |
1630 // Queue a cursor reposition event; this must wait until after DoRelayout()
1631 mImpl->mTextInput->mUpdateCursorPosition = true;
1634 void Controller::UpdateModel( OperationsMask operationsRequired )
1636 // Calculate the operations to be done.
1637 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1639 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1641 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1643 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1644 if( GET_LINE_BREAKS & operations )
1646 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1647 // calculate the bidirectional info for each 'paragraph'.
1648 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1649 // is not shaped together).
1650 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1652 SetLineBreakInfo( utf32Characters,
1656 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1657 if( GET_WORD_BREAKS & operations )
1659 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1660 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1662 SetWordBreakInfo( utf32Characters,
1666 const bool getScripts = GET_SCRIPTS & operations;
1667 const bool validateFonts = VALIDATE_FONTS & operations;
1669 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1670 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1672 if( getScripts || validateFonts )
1674 // Validates the fonts assigned by the application or assigns default ones.
1675 // It makes sure all the characters are going to be rendered by the correct font.
1676 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1680 // Retrieves the scripts used in the text.
1681 multilanguageSupport.SetScripts( utf32Characters,
1688 if( 0u == validFonts.Count() )
1690 // Copy the requested font defaults received via the property system.
1691 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1692 GetDefaultFonts( validFonts, numberOfCharacters );
1695 // Validates the fonts. If there is a character with no assigned font it sets a default one.
1696 // After this call, fonts are validated.
1697 multilanguageSupport.ValidateFonts( utf32Characters,
1703 Vector<Character> mirroredUtf32Characters;
1704 bool textMirrored = false;
1705 if( BIDI_INFO & operations )
1707 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1708 // bidirectional info.
1710 Length numberOfParagraphs = 0u;
1712 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1713 for( Length index = 0u; index < numberOfCharacters; ++index )
1715 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1717 ++numberOfParagraphs;
1721 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1722 bidirectionalInfo.Reserve( numberOfParagraphs );
1724 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1725 SetBidirectionalInfo( utf32Characters,
1728 bidirectionalInfo );
1730 if( 0u != bidirectionalInfo.Count() )
1732 // This paragraph has right to left text. Some characters may need to be mirrored.
1733 // TODO: consider if the mirrored string can be stored as well.
1735 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1737 // Only set the character directions if there is right to left characters.
1738 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
1739 directions.Resize( numberOfCharacters );
1741 GetCharactersDirection( bidirectionalInfo,
1746 // There is no right to left characters. Clear the directions vector.
1747 mImpl->mLogicalModel->mCharacterDirections.Clear();
1752 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1753 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1754 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1755 if( SHAPE_TEXT & operations )
1757 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1759 ShapeText( textToShape,
1764 glyphsToCharactersMap,
1765 charactersPerGlyph );
1767 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1768 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1769 mImpl->mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
1772 const Length numberOfGlyphs = glyphs.Count();
1774 if( GET_GLYPH_METRICS & operations )
1776 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1780 bool Controller::DoRelayout( const Size& size,
1781 OperationsMask operationsRequired,
1784 bool viewUpdated( false );
1786 // Calculate the operations to be done.
1787 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1789 if( LAYOUT & operations )
1791 // Some vectors with data needed to layout and reorder may be void
1792 // after the first time the text has been laid out.
1793 // Fill the vectors again.
1795 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1797 if( 0u == numberOfGlyphs )
1799 // Nothing else to do if there is no glyphs.
1803 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1804 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1805 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1806 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1807 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1809 // Set the layout parameters.
1810 LayoutParameters layoutParameters( size,
1811 mImpl->mLogicalModel->mText.Begin(),
1812 lineBreakInfo.Begin(),
1813 wordBreakInfo.Begin(),
1816 glyphsToCharactersMap.Begin(),
1817 charactersPerGlyph.Begin() );
1819 // The laid-out lines.
1820 // It's not possible to know in how many lines the text is going to be laid-out,
1821 // but it can be resized at least with the number of 'paragraphs' to avoid
1822 // some re-allocations.
1823 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1825 // Delete any previous laid out lines before setting the new ones.
1828 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1829 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1831 // Resize the vector of positions to have the same size than the vector of glyphs.
1832 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1833 glyphPositions.Resize( numberOfGlyphs );
1835 // Update the visual model.
1836 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1843 // Reorder the lines
1844 if( REORDER & operations )
1846 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1848 // Check first if there are paragraphs with bidirectional info.
1849 if( 0u != bidirectionalInfo.Count() )
1852 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1854 // Reorder the lines.
1855 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1856 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1857 ReorderLines( bidirectionalInfo,
1859 lineBidirectionalInfoRuns );
1861 // Set the bidirectional info into the model.
1862 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1863 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1864 numberOfBidirectionalInfoRuns );
1866 // Set the bidirectional info per line into the layout parameters.
1867 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1868 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1870 // Get the character to glyph conversion table and set into the layout.
1871 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1873 // Get the glyphs per character table and set into the layout.
1874 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1876 // Re-layout the text. Reorder those lines with right to left characters.
1877 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1880 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1881 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1882 endIt = lineBidirectionalInfoRuns.End();
1886 BidirectionalLineInfoRun& bidiLineInfo = *it;
1888 free( bidiLineInfo.visualToLogicalMap );
1893 if( ALIGN & operations )
1895 mImpl->mLayoutEngine.Align( layoutParameters,
1901 // Sets the actual size.
1902 if( UPDATE_ACTUAL_SIZE & operations )
1904 mImpl->mVisualModel->SetActualSize( layoutSize );
1910 layoutSize = mImpl->mVisualModel->GetActualSize();
1916 void Controller::CalculateTextAlignment( const Size& size )
1918 // Get the direction of the first character.
1919 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1921 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1923 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1924 LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1925 if( firstParagraphDirection &&
1926 ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1928 if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1930 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1934 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1938 switch( horizontalAlignment )
1940 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1942 mImpl->mAlignmentOffset.x = 0.f;
1945 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1947 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1948 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1951 case LayoutEngine::HORIZONTAL_ALIGN_END:
1953 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1958 const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1959 switch( verticalAlignment )
1961 case LayoutEngine::VERTICAL_ALIGN_TOP:
1963 mImpl->mAlignmentOffset.y = 0.f;
1966 case LayoutEngine::VERTICAL_ALIGN_CENTER:
1968 const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1969 mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1972 case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1974 mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1980 View& Controller::GetView()
1982 return mImpl->mView;
1985 LayoutEngine& Controller::GetLayoutEngine()
1987 return mImpl->mLayoutEngine;
1990 void Controller::RequestRelayout()
1992 mImpl->mControlInterface.RequestTextRelayout();
1995 void Controller::KeyboardFocusGainEvent()
1997 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
1999 if( mImpl->mTextInput )
2001 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
2002 mImpl->mTextInput->mEventQueue.push_back( event );
2008 void Controller::KeyboardFocusLostEvent()
2010 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
2012 if( mImpl->mTextInput )
2014 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
2015 mImpl->mTextInput->mEventQueue.push_back( event );
2021 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
2023 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
2025 if( mImpl->mTextInput &&
2026 keyEvent.state == KeyEvent::Down )
2028 int keyCode = keyEvent.keyCode;
2029 const std::string& keyString = keyEvent.keyPressed;
2031 // Pre-process to separate modifying events from non-modifying input events.
2032 if( Dali::DALI_KEY_ESCAPE == keyCode )
2034 // Escape key is a special case which causes focus loss
2035 KeyboardFocusLostEvent();
2037 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
2038 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
2039 Dali::DALI_KEY_CURSOR_UP == keyCode ||
2040 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
2042 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
2043 event.p1.mInt = keyCode;
2044 mImpl->mTextInput->mEventQueue.push_back( event );
2046 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
2048 // Queue a delete event
2050 event.type = DELETE_TEXT;
2051 mImpl->mModifyEvents.push_back( event );
2053 else if( !keyString.empty() )
2055 // Queue an insert event
2057 event.type = INSERT_TEXT;
2058 event.text = keyString;
2059 mImpl->mModifyEvents.push_back( event );
2062 mImpl->mTextInput->ChangeState( TextInput::EDITING ); // todo Confirm this is the best place to change the state of
2070 void Controller::TapEvent( unsigned int tapCount, float x, float y )
2072 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
2074 if( mImpl->mTextInput )
2076 TextInput::Event event( TextInput::TAP_EVENT );
2077 event.p1.mUint = tapCount;
2078 event.p2.mFloat = x;
2079 event.p3.mFloat = y;
2080 mImpl->mTextInput->mEventQueue.push_back( event );
2086 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
2088 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
2090 if( mImpl->mTextInput )
2092 TextInput::Event event( TextInput::PAN_EVENT );
2093 event.p1.mInt = state;
2094 event.p2.mFloat = displacement.x;
2095 event.p3.mFloat = displacement.y;
2096 mImpl->mTextInput->mEventQueue.push_back( event );
2102 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
2104 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
2106 if( mImpl->mTextInput )
2108 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
2109 event.p1.mUint = state;
2110 event.p2.mFloat = x;
2111 event.p3.mFloat = y;
2112 mImpl->mTextInput->mEventQueue.push_back( event );
2118 Controller::~Controller()
2123 Controller::Controller( ControlInterface& controlInterface )
2126 mImpl = new Controller::Impl( controlInterface );
2131 } // namespace Toolkit