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 );
990 mVisualModel->SetUnderlineHeight( 0.0f );
998 ControlInterface& mControlInterface; ///< Reference to the text controller.
999 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
1000 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
1001 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
1002 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
1003 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
1004 View mView; ///< The view interface to the rendering back-end.
1005 LayoutEngine mLayoutEngine; ///< The layout engine.
1006 std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
1007 Size mControlSize; ///< The size of the control.
1008 Vector2 mAlignmentOffset; ///< Vertical and horizontal offset of the whole text inside the control due to alignment.
1009 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
1010 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
1013 ControllerPtr Controller::New( ControlInterface& controlInterface )
1015 return ControllerPtr( new Controller( controlInterface ) );
1018 void Controller::SetText( const std::string& text )
1020 // Cancel previously queued inserts etc.
1021 mImpl->mModifyEvents.clear();
1023 // Keep until size negotiation
1025 event.type = REPLACE_TEXT;
1027 mImpl->mModifyEvents.push_back( event );
1029 if( mImpl->mTextInput )
1031 // Cancel previously queued events
1032 mImpl->mTextInput->mEventQueue.clear();
1034 // TODO - Hide selection decorations
1038 void Controller::GetText( std::string& text ) const
1040 if( !mImpl->mModifyEvents.empty() &&
1041 REPLACE_TEXT == mImpl->mModifyEvents[0].type )
1043 text = mImpl->mModifyEvents[0].text;
1047 // TODO - Convert from UTF-32
1051 void Controller::SetPlaceholderText( const std::string& text )
1053 if( !mImpl->mTextInput )
1055 mImpl->mTextInput->mPlaceholderText = text;
1059 void Controller::GetPlaceholderText( std::string& text ) const
1061 if( !mImpl->mTextInput )
1063 text = mImpl->mTextInput->mPlaceholderText;
1067 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
1069 if( !mImpl->mFontDefaults )
1071 mImpl->mFontDefaults = new Controller::FontDefaults();
1074 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
1075 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1076 mImpl->mOperationsPending = ALL_OPERATIONS;
1077 mImpl->mRecalculateNaturalSize = true;
1079 // Clear the font-specific data
1080 mImpl->mLogicalModel->mFontRuns.Clear();
1081 mImpl->mVisualModel->mGlyphs.Clear();
1082 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1083 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1084 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1085 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1086 mImpl->mVisualModel->mGlyphPositions.Clear();
1087 mImpl->mVisualModel->mLines.Clear();
1088 mImpl->mVisualModel->ClearCaches();
1093 const std::string& Controller::GetDefaultFontFamily() const
1095 if( mImpl->mFontDefaults )
1097 return mImpl->mFontDefaults->mDefaultFontFamily;
1100 return String::EMPTY;;
1103 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
1105 if( !mImpl->mFontDefaults )
1107 mImpl->mFontDefaults = new Controller::FontDefaults();
1110 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
1111 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1112 mImpl->mOperationsPending = ALL_OPERATIONS;
1113 mImpl->mRecalculateNaturalSize = true;
1115 // Clear the font-specific data
1116 mImpl->mLogicalModel->mFontRuns.Clear();
1117 mImpl->mVisualModel->mGlyphs.Clear();
1118 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1119 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1120 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1121 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1122 mImpl->mVisualModel->mGlyphPositions.Clear();
1123 mImpl->mVisualModel->mLines.Clear();
1124 mImpl->mVisualModel->ClearCaches();
1129 const std::string& Controller::GetDefaultFontStyle() const
1131 if( mImpl->mFontDefaults )
1133 return mImpl->mFontDefaults->mDefaultFontStyle;
1136 return String::EMPTY;
1139 void Controller::SetDefaultPointSize( float pointSize )
1141 if( !mImpl->mFontDefaults )
1143 mImpl->mFontDefaults = new Controller::FontDefaults();
1146 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
1147 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1148 mImpl->mOperationsPending = ALL_OPERATIONS;
1149 mImpl->mRecalculateNaturalSize = true;
1151 // Clear the font-specific data
1152 mImpl->mLogicalModel->mFontRuns.Clear();
1153 mImpl->mVisualModel->mGlyphs.Clear();
1154 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1155 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1156 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1157 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1158 mImpl->mVisualModel->mGlyphPositions.Clear();
1159 mImpl->mVisualModel->mLines.Clear();
1160 mImpl->mVisualModel->ClearCaches();
1165 float Controller::GetDefaultPointSize() const
1167 if( mImpl->mFontDefaults )
1169 return mImpl->mFontDefaults->mDefaultPointSize;
1175 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters ) const
1177 if( mImpl->mFontDefaults )
1180 fontRun.characterRun.characterIndex = 0;
1181 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
1182 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
1183 fontRun.isDefault = true;
1185 fonts.PushBack( fontRun );
1189 const Vector4& Controller::GetTextColor() const
1191 return mImpl->mVisualModel->GetTextColor();
1194 const Vector2& Controller::GetShadowOffset() const
1196 return mImpl->mVisualModel->GetShadowOffset();
1199 const Vector4& Controller::GetShadowColor() const
1201 return mImpl->mVisualModel->GetShadowColor();
1204 const Vector4& Controller::GetUnderlineColor() const
1206 return mImpl->mVisualModel->GetUnderlineColor();
1209 bool Controller::IsUnderlineEnabled() const
1211 return mImpl->mVisualModel->IsUnderlineEnabled();
1214 float Controller::GetUnderlineHeight() const
1216 return mImpl->mVisualModel->GetUnderlineHeight();
1219 void Controller::SetTextColor( const Vector4& textColor )
1221 mImpl->mVisualModel->SetTextColor( textColor );
1224 void Controller::SetShadowOffset( const Vector2& shadowOffset )
1226 mImpl->mVisualModel->SetShadowOffset( shadowOffset );
1229 void Controller::SetShadowColor( const Vector4& shadowColor )
1231 mImpl->mVisualModel->SetShadowColor( shadowColor );
1234 void Controller::SetUnderlineColor( const Vector4& color )
1236 mImpl->mVisualModel->SetUnderlineColor( color );
1239 void Controller::SetUnderlineEnabled( bool enabled )
1241 mImpl->mVisualModel->SetUnderlineEnabled( enabled );
1244 void Controller::SetUnderlineHeight( float height )
1246 mImpl->mVisualModel->SetUnderlineHeight( height );
1249 void Controller::EnableTextInput( DecoratorPtr decorator )
1251 if( !mImpl->mTextInput )
1253 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel,
1254 mImpl->mVisualModel,
1256 mImpl->mFontDefaults,
1257 mImpl->mFontClient );
1261 void Controller::SetEnableCursorBlink( bool enable )
1263 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
1265 if( mImpl->mTextInput )
1267 mImpl->mTextInput->mCursorBlinkEnabled = enable;
1270 mImpl->mTextInput->mDecorator )
1272 mImpl->mTextInput->mDecorator->StopCursorBlink();
1277 bool Controller::GetEnableCursorBlink() const
1279 if( mImpl->mTextInput )
1281 return mImpl->mTextInput->mCursorBlinkEnabled;
1287 const Vector2& Controller::GetScrollPosition() const
1289 if( mImpl->mTextInput )
1291 return mImpl->mTextInput->mScrollPosition;
1294 return Vector2::ZERO;
1297 const Vector2& Controller::GetAlignmentOffset() const
1299 return mImpl->mAlignmentOffset;
1302 Vector3 Controller::GetNaturalSize()
1304 Vector3 naturalSize;
1306 // Make sure the model is up-to-date before layouting
1307 ProcessModifyEvents();
1309 if( mImpl->mRecalculateNaturalSize )
1311 // Operations that can be done only once until the text changes.
1312 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1319 GET_GLYPH_METRICS );
1320 // Make sure the model is up-to-date before layouting
1321 UpdateModel( onlyOnceOperations );
1323 // Operations that need to be done if the size changes.
1324 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
1328 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
1329 static_cast<OperationsMask>( onlyOnceOperations |
1331 naturalSize.GetVectorXY() );
1333 // Do not do again the only once operations.
1334 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1336 // Do the size related operations again.
1337 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1339 // Stores the natural size to avoid recalculate it again
1340 // unless the text/style changes.
1341 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
1343 mImpl->mRecalculateNaturalSize = false;
1347 naturalSize = mImpl->mVisualModel->GetNaturalSize();
1353 float Controller::GetHeightForWidth( float width )
1355 // Make sure the model is up-to-date before layouting
1356 ProcessModifyEvents();
1359 if( width != mImpl->mControlSize.width )
1361 // Operations that can be done only once until the text changes.
1362 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
1369 GET_GLYPH_METRICS );
1370 // Make sure the model is up-to-date before layouting
1371 UpdateModel( onlyOnceOperations );
1373 // Operations that need to be done if the size changes.
1374 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
1378 DoRelayout( Size( width, MAX_FLOAT ),
1379 static_cast<OperationsMask>( onlyOnceOperations |
1383 // Do not do again the only once operations.
1384 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1386 // Do the size related operations again.
1387 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1391 layoutSize = mImpl->mVisualModel->GetActualSize();
1394 return layoutSize.height;
1397 bool Controller::Relayout( const Size& size )
1399 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
1401 bool glyphsRemoved( false );
1402 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
1404 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
1405 glyphsRemoved = true;
1408 // Not worth to relayout if width or height is equal to zero.
1409 return glyphsRemoved;
1412 if( size != mImpl->mControlSize )
1414 // Operations that need to be done if the size changes.
1415 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1418 UPDATE_ACTUAL_SIZE |
1421 mImpl->mControlSize = size;
1424 // Make sure the model is up-to-date before layouting
1425 ProcessModifyEvents();
1426 UpdateModel( mImpl->mOperationsPending );
1429 bool updated = DoRelayout( mImpl->mControlSize,
1430 mImpl->mOperationsPending,
1433 // Do not re-do any operation until something changes.
1434 mImpl->mOperationsPending = NO_OPERATION;
1436 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
1437 CalculateTextAlignment( size );
1439 if( mImpl->mTextInput )
1441 // Move the cursor, grab handle etc.
1442 updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize, mImpl->mAlignmentOffset ) || updated;
1448 void Controller::ProcessModifyEvents()
1450 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
1452 for( unsigned int i=0; i<events.size(); ++i )
1454 if( REPLACE_TEXT == events[0].type )
1456 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
1457 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
1459 ReplaceTextEvent( events[0].text );
1461 else if( INSERT_TEXT == events[0].type )
1463 InsertTextEvent( events[0].text );
1465 else if( DELETE_TEXT == events[0].type )
1471 // Discard temporary text
1475 void Controller::ReplaceTextEvent( const std::string& text )
1478 mImpl->mLogicalModel->mText.Clear();
1479 mImpl->mLogicalModel->mScriptRuns.Clear();
1480 mImpl->mLogicalModel->mFontRuns.Clear();
1481 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1482 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1483 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1484 mImpl->mLogicalModel->mCharacterDirections.Clear();
1485 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1486 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1487 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1488 mImpl->mVisualModel->mGlyphs.Clear();
1489 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1490 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1491 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1492 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1493 mImpl->mVisualModel->mGlyphPositions.Clear();
1494 mImpl->mVisualModel->mLines.Clear();
1495 mImpl->mVisualModel->ClearCaches();
1497 // Convert text into UTF-32
1498 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1499 utf32Characters.Resize( text.size() );
1501 // This is a bit horrible but std::string returns a (signed) char*
1502 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1504 // Transform a text array encoded in utf8 into an array encoded in utf32.
1505 // It returns the actual number of characters.
1506 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1507 utf32Characters.Resize( characterCount );
1509 // Reset the cursor position
1510 if( mImpl->mTextInput )
1512 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
1513 // TODO - handle secondary cursor
1516 // The natural size needs to be re-calculated.
1517 mImpl->mRecalculateNaturalSize = true;
1519 // Apply modifications to the model
1520 mImpl->mOperationsPending = ALL_OPERATIONS;
1521 UpdateModel( ALL_OPERATIONS );
1522 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1524 UPDATE_ACTUAL_SIZE |
1528 void Controller::InsertTextEvent( const std::string& text )
1530 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1532 // TODO - Optimize this
1533 mImpl->mLogicalModel->mScriptRuns.Clear();
1534 mImpl->mLogicalModel->mFontRuns.Clear();
1535 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1536 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1537 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1538 mImpl->mLogicalModel->mCharacterDirections.Clear();
1539 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1540 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1541 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1542 mImpl->mVisualModel->mGlyphs.Clear();
1543 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1544 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1545 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1546 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1547 mImpl->mVisualModel->mGlyphPositions.Clear();
1548 mImpl->mVisualModel->mLines.Clear();
1549 mImpl->mVisualModel->ClearCaches();
1551 // Convert text into UTF-32
1552 Vector<Character> utf32Characters;
1553 utf32Characters.Resize( text.size() );
1555 // This is a bit horrible but std::string returns a (signed) char*
1556 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1558 // Transform a text array encoded in utf8 into an array encoded in utf32.
1559 // It returns the actual number of characters.
1560 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1561 utf32Characters.Resize( characterCount );
1563 // Insert at current cursor position
1564 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1565 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1567 if( cursorIndex < modifyText.Count() )
1569 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1573 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1576 // Advance the cursor position
1579 // The natural size needs to be re-calculated.
1580 mImpl->mRecalculateNaturalSize = true;
1582 // Apply modifications to the model; TODO - Optimize this
1583 mImpl->mOperationsPending = ALL_OPERATIONS;
1584 UpdateModel( ALL_OPERATIONS );
1585 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1587 UPDATE_ACTUAL_SIZE |
1590 // Queue a cursor reposition event; this must wait until after DoRelayout()
1591 mImpl->mTextInput->mUpdateCursorPosition = true;
1594 void Controller::DeleteTextEvent()
1596 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1598 // TODO - Optimize this
1599 mImpl->mLogicalModel->mScriptRuns.Clear();
1600 mImpl->mLogicalModel->mFontRuns.Clear();
1601 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1602 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1603 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1604 mImpl->mLogicalModel->mCharacterDirections.Clear();
1605 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1606 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1607 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1608 mImpl->mVisualModel->mGlyphs.Clear();
1609 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1610 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1611 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1612 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1613 mImpl->mVisualModel->mGlyphPositions.Clear();
1614 mImpl->mVisualModel->mLines.Clear();
1615 mImpl->mVisualModel->ClearCaches();
1617 // Delte at current cursor position
1618 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1619 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1621 if( cursorIndex > 0 &&
1622 cursorIndex-1 < modifyText.Count() )
1624 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1626 // Cursor position retreat
1630 // The natural size needs to be re-calculated.
1631 mImpl->mRecalculateNaturalSize = true;
1633 // Apply modifications to the model; TODO - Optimize this
1634 mImpl->mOperationsPending = ALL_OPERATIONS;
1635 UpdateModel( ALL_OPERATIONS );
1636 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1638 UPDATE_ACTUAL_SIZE |
1641 // Queue a cursor reposition event; this must wait until after DoRelayout()
1642 mImpl->mTextInput->mUpdateCursorPosition = true;
1645 void Controller::UpdateModel( OperationsMask operationsRequired )
1647 // Calculate the operations to be done.
1648 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1650 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1652 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1654 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1655 if( GET_LINE_BREAKS & operations )
1657 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1658 // calculate the bidirectional info for each 'paragraph'.
1659 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1660 // is not shaped together).
1661 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1663 SetLineBreakInfo( utf32Characters,
1667 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1668 if( GET_WORD_BREAKS & operations )
1670 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1671 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1673 SetWordBreakInfo( utf32Characters,
1677 const bool getScripts = GET_SCRIPTS & operations;
1678 const bool validateFonts = VALIDATE_FONTS & operations;
1680 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1681 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1683 if( getScripts || validateFonts )
1685 // Validates the fonts assigned by the application or assigns default ones.
1686 // It makes sure all the characters are going to be rendered by the correct font.
1687 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1691 // Retrieves the scripts used in the text.
1692 multilanguageSupport.SetScripts( utf32Characters,
1699 if( 0u == validFonts.Count() )
1701 // Copy the requested font defaults received via the property system.
1702 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1703 GetDefaultFonts( validFonts, numberOfCharacters );
1706 // Validates the fonts. If there is a character with no assigned font it sets a default one.
1707 // After this call, fonts are validated.
1708 multilanguageSupport.ValidateFonts( utf32Characters,
1714 Vector<Character> mirroredUtf32Characters;
1715 bool textMirrored = false;
1716 if( BIDI_INFO & operations )
1718 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1719 // bidirectional info.
1721 Length numberOfParagraphs = 0u;
1723 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1724 for( Length index = 0u; index < numberOfCharacters; ++index )
1726 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1728 ++numberOfParagraphs;
1732 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1733 bidirectionalInfo.Reserve( numberOfParagraphs );
1735 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1736 SetBidirectionalInfo( utf32Characters,
1739 bidirectionalInfo );
1741 if( 0u != bidirectionalInfo.Count() )
1743 // This paragraph has right to left text. Some characters may need to be mirrored.
1744 // TODO: consider if the mirrored string can be stored as well.
1746 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1748 // Only set the character directions if there is right to left characters.
1749 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
1750 directions.Resize( numberOfCharacters );
1752 GetCharactersDirection( bidirectionalInfo,
1757 // There is no right to left characters. Clear the directions vector.
1758 mImpl->mLogicalModel->mCharacterDirections.Clear();
1763 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1764 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1765 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1766 if( SHAPE_TEXT & operations )
1768 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1770 ShapeText( textToShape,
1775 glyphsToCharactersMap,
1776 charactersPerGlyph );
1778 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1779 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1780 mImpl->mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
1783 const Length numberOfGlyphs = glyphs.Count();
1785 if( GET_GLYPH_METRICS & operations )
1787 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1791 bool Controller::DoRelayout( const Size& size,
1792 OperationsMask operationsRequired,
1795 bool viewUpdated( false );
1797 // Calculate the operations to be done.
1798 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1800 if( LAYOUT & operations )
1802 // Some vectors with data needed to layout and reorder may be void
1803 // after the first time the text has been laid out.
1804 // Fill the vectors again.
1806 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1808 if( 0u == numberOfGlyphs )
1810 // Nothing else to do if there is no glyphs.
1814 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1815 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1816 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1817 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1818 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1820 // Set the layout parameters.
1821 LayoutParameters layoutParameters( size,
1822 mImpl->mLogicalModel->mText.Begin(),
1823 lineBreakInfo.Begin(),
1824 wordBreakInfo.Begin(),
1827 glyphsToCharactersMap.Begin(),
1828 charactersPerGlyph.Begin() );
1830 // The laid-out lines.
1831 // It's not possible to know in how many lines the text is going to be laid-out,
1832 // but it can be resized at least with the number of 'paragraphs' to avoid
1833 // some re-allocations.
1834 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1836 // Delete any previous laid out lines before setting the new ones.
1839 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1840 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1842 // Resize the vector of positions to have the same size than the vector of glyphs.
1843 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1844 glyphPositions.Resize( numberOfGlyphs );
1846 // Update the visual model.
1847 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1854 // Reorder the lines
1855 if( REORDER & operations )
1857 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1859 // Check first if there are paragraphs with bidirectional info.
1860 if( 0u != bidirectionalInfo.Count() )
1863 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1865 // Reorder the lines.
1866 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1867 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1868 ReorderLines( bidirectionalInfo,
1870 lineBidirectionalInfoRuns );
1872 // Set the bidirectional info into the model.
1873 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1874 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1875 numberOfBidirectionalInfoRuns );
1877 // Set the bidirectional info per line into the layout parameters.
1878 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1879 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1881 // Get the character to glyph conversion table and set into the layout.
1882 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1884 // Get the glyphs per character table and set into the layout.
1885 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1887 // Re-layout the text. Reorder those lines with right to left characters.
1888 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1891 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1892 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1893 endIt = lineBidirectionalInfoRuns.End();
1897 BidirectionalLineInfoRun& bidiLineInfo = *it;
1899 free( bidiLineInfo.visualToLogicalMap );
1904 if( ALIGN & operations )
1906 mImpl->mLayoutEngine.Align( layoutParameters,
1912 // Sets the actual size.
1913 if( UPDATE_ACTUAL_SIZE & operations )
1915 mImpl->mVisualModel->SetActualSize( layoutSize );
1921 layoutSize = mImpl->mVisualModel->GetActualSize();
1927 void Controller::CalculateTextAlignment( const Size& size )
1929 // Get the direction of the first character.
1930 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1932 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1934 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1935 LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1936 if( firstParagraphDirection &&
1937 ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1939 if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1941 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1945 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1949 switch( horizontalAlignment )
1951 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1953 mImpl->mAlignmentOffset.x = 0.f;
1956 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1958 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1959 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1962 case LayoutEngine::HORIZONTAL_ALIGN_END:
1964 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1969 const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1970 switch( verticalAlignment )
1972 case LayoutEngine::VERTICAL_ALIGN_TOP:
1974 mImpl->mAlignmentOffset.y = 0.f;
1977 case LayoutEngine::VERTICAL_ALIGN_CENTER:
1979 const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1980 mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1983 case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1985 mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1991 View& Controller::GetView()
1993 return mImpl->mView;
1996 LayoutEngine& Controller::GetLayoutEngine()
1998 return mImpl->mLayoutEngine;
2001 void Controller::RequestRelayout()
2003 mImpl->mControlInterface.RequestTextRelayout();
2006 void Controller::KeyboardFocusGainEvent()
2008 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
2010 if( mImpl->mTextInput )
2012 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
2013 mImpl->mTextInput->mEventQueue.push_back( event );
2019 void Controller::KeyboardFocusLostEvent()
2021 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
2023 if( mImpl->mTextInput )
2025 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
2026 mImpl->mTextInput->mEventQueue.push_back( event );
2032 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
2034 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
2036 if( mImpl->mTextInput &&
2037 keyEvent.state == KeyEvent::Down )
2039 int keyCode = keyEvent.keyCode;
2040 const std::string& keyString = keyEvent.keyPressed;
2042 // Pre-process to separate modifying events from non-modifying input events.
2043 if( Dali::DALI_KEY_ESCAPE == keyCode )
2045 // Escape key is a special case which causes focus loss
2046 KeyboardFocusLostEvent();
2048 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
2049 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
2050 Dali::DALI_KEY_CURSOR_UP == keyCode ||
2051 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
2053 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
2054 event.p1.mInt = keyCode;
2055 mImpl->mTextInput->mEventQueue.push_back( event );
2057 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
2059 // Queue a delete event
2061 event.type = DELETE_TEXT;
2062 mImpl->mModifyEvents.push_back( event );
2064 else if( !keyString.empty() )
2066 // Queue an insert event
2068 event.type = INSERT_TEXT;
2069 event.text = keyString;
2070 mImpl->mModifyEvents.push_back( event );
2073 mImpl->mTextInput->ChangeState( TextInput::EDITING ); // todo Confirm this is the best place to change the state of
2081 void Controller::TapEvent( unsigned int tapCount, float x, float y )
2083 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
2085 if( mImpl->mTextInput )
2087 TextInput::Event event( TextInput::TAP_EVENT );
2088 event.p1.mUint = tapCount;
2089 event.p2.mFloat = x;
2090 event.p3.mFloat = y;
2091 mImpl->mTextInput->mEventQueue.push_back( event );
2097 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
2099 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
2101 if( mImpl->mTextInput )
2103 TextInput::Event event( TextInput::PAN_EVENT );
2104 event.p1.mInt = state;
2105 event.p2.mFloat = displacement.x;
2106 event.p3.mFloat = displacement.y;
2107 mImpl->mTextInput->mEventQueue.push_back( event );
2113 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
2115 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
2117 if( mImpl->mTextInput )
2119 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
2120 event.p1.mUint = state;
2121 event.p2.mFloat = x;
2122 event.p3.mFloat = y;
2123 mImpl->mTextInput->mEventQueue.push_back( event );
2129 Controller::~Controller()
2134 Controller::Controller( ControlInterface& controlInterface )
2137 mImpl = new Controller::Impl( controlInterface );
2142 } // namespace Toolkit